安卓客户端socket上传文件到c++服务端
目前只研究出了一种:用socket方式实现: 客户端上传文件到服务端的方法。
大致思路:
安卓打开文件流将文件以二进制字符串格式读出,转化为Base64格式后传输,c++服务端接受Base64格式字符串后转化为一般文件格式字符串后写入文件。
安卓先创建文件流
FileInputStream stream;
stream=new FileInputStream(path);//path为文件路径,需要app获取存储权限,否则会报错eacces permission denied
接着创建一个byte数组
byte[] b=new byte[30720];
读取文件流中的数据,将读到的内容存入byte数组中
for(;(length=stream.read(b))!=-1;) {
}
上面这个循环表明不断从文件读取大小为“byte数组长度”的数据块,read会返回读取的长度,读完了read函数会返回-1从而结束
【关键点】将读取到的byte(二进制)数组内的数据 转换为:Base64格式字符串
为什么?因为如果直接将byte数组强制转换为字符串格式后会导致部分不可读不可传输等错误。例如如下代码
String content=new String(b);
这样通过socket传输后c++服务端难以读取全部字符串
因此使用以下代码将byte字符串转为Base64格式后方可传输
String content=Base64.encodeToString(b, 0,b.length, Base64.DEFAULT);
将字符串content发送出去即可。
如果文件过大,需要分批传输,使用以下代码
try{
FileInputStream stream;
stream=new FileInputStream(path);//需要app获取存储权限,否则会报错eacces permission denied
byte[] b=new byte[30720];
for(;(length=stream.read(b))!=-1;) {
content=Base64.encodeToString(b, 0,b.length, Base64.DEFAULT);
//这里执行发送,先将content数据发送出去
stream.close();
catch (java.io.FileNotFoundException e)
android.widget.Toast.makeText(activity, e.getMessage(), android.widget.Toast.LENGTH_SHORT).show();
break;
catch (java.lang.NullPointerException e)
android.widget.Toast.makeText(activity, e.getMessage(), android.widget.Toast.LENGTH_SHORT).show();
break;
catch (java.io.IOException e)
android.widget.Toast.makeText(activity, e.getMessage(), android.widget.Toast.LENGTH_SHORT).show();
break;
}
即,在文件读取的循环中不断创建新的byte数组流用于存放当前的部分文件数据块,发送后就关闭该数据流,创建新流读取下一块数据.
c++端
参考了这里的: C++读写图片数据转成Base64格式的一种方法 - 码农小丁的程序世界 - 博客园
CreateFile()创建文件流,用recv()函数读取客户端发来Base64格式数据,将Base64格式数据转化为一般字符串格式,写入文件
安卓发回的数据有时候会出错!即原本最后一位应该是'\n',但如果不是,则本次数据传输出错!对此研究不深,貌似不会太大改变文件,只会在字节大小上有出入
以下代码为样例,里面转化Base64格式为普通格式字符串的函数需要用到上面参考网页上的Base64.h头文件,#include "Base64.h"加进去就行
HANDLE hFile;
hFile = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)break;
while(true)
char kk[66000]={0};
if(recv(g_ClientSocket[c1->member_No-1], kk, sizeof(kk), 0) <= 0)
{cout<<"socket断开!\n";break;}
if(……)……//如果是结束传输文件特殊字符串,跳出死循环!
if(kk[strlen(kk)-1]=='\n')
kk[strlen(kk)-1]='\0';//安卓发回数据最后一位为'\n'
cout<<kk[strlen(kk)-1]<<"|"<<cout<<kk[strlen(kk)]<<"|";
CBase64 base64;
int datalen(0);
DWORD dwritelen(0);
std::string strdcode = base64.Decode(kk,strlen(kk), datalen);//转化Base64格式为普通格式字符串
if (!WriteFile(hFile, strdcode.data(), datalen, &dwritelen, NULL))
CloseHandle(hFile);
break;
//fclose(fp);
CloseHandle(hFile);
base64.h
#ifndef INCLUDE_Base64_H
#define INCLUDE_Base64_H
//base64转换方法
//https://www.cnblogs.com/jeray/p/8746976.html
class CBase64
public:
public:
CBase64();
~CBase64();
DataByte
[in]输入的数据长度,以字节为单位
std::string Encode(const char* Data, int DataByte);
DataByte
[in]输入的数据长度,以字节为单位
OutByte
[out]输出的数据长度,以字节为单位,请不要通过返回值计算
输出数据的长度
std::string Decode(const char* Data, int DataByte, int& OutByte);
//++Base64.cpp
CBase64::CBase64()
CBase64::~CBase64()
std::string CBase64::Encode(const char* Data, int DataByte)
//编码表
const char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//返回值
string strEncode;
unsigned char Tmp[4] = { 0 };
int LineLength = 0;
for (int i = 0; i<(int)(DataByte / 3); i++)
Tmp[1] = *Data++;
Tmp[2] = *Data++;
Tmp[3] = *Data++;
strEncode += EncodeTable[Tmp[1] >> 2];
strEncode += EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F];
strEncode += EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F];
strEncode += EncodeTable[Tmp[3] & 0x3F];
if (LineLength += 4, LineLength == 76) { strEncode += "\r\n"; LineLength = 0; }
//对剩余数据进行编码
int Mod = DataByte % 3;
if (Mod == 1)
Tmp[1] = *Data++;
strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode += EncodeTable[((Tmp[1] & 0x03) << 4)];
strEncode += "==";
else if (Mod == 2)
Tmp[1] = *Data++;
Tmp[2] = *Data++;
strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode += EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)];
strEncode += EncodeTable[((Tmp[2] & 0x0F) << 2)];
strEncode += "=";
return strEncode;
std::string CBase64::Decode(const char* Data, int DataByte, int& OutByte)
//解码表
const char DecodeTable[] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62, // '+'
0, 0, 0,
63, // '/'
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'-'9'
0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'-'Z'
0, 0, 0, 0, 0, 0,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 'a'-'z'
//返回值
string strDecode;
int nValue;
int i = 0;
while (i < DataByte)
if (*Data != '\r' && *Data != '\n')
nValue = DecodeTable[*Data++] << 18;
nValue += DecodeTable[*Data++] << 12;
strDecode += (nValue & 0x00FF0000) >> 16;
OutByte++;
if (*Data != '=')
nValue += DecodeTable[*Data++] << 6;
strDecode += (nValue & 0x0000FF00) >> 8;
OutByte++;
if (*Data != '=')
nValue += DecodeTable[*Data++];
strDecode += nValue & 0x000000FF;
OutByte++;
i += 4;
else// 回车换行,跳过
Data++;
return strDecode;
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const char padding_char = '=';
inline int num_strchr(const char *str, char c) //
const char *pindex = strchr(str, c);
if (NULL == pindex)
return -1;
return pindex - str;
void base64_decode(const char * base64, unsigned char * dedata)
int i = 0, j=0;
int trans[4] = {0,0,0,0};
for (;base64[i]!='\0';i+=4){
// 每四个一组,译码成三个字符
trans[0] = num_strchr(base64char, base64[i]);
trans[1] = num_strchr(base64char, base64[i+1]);
// 1/3
dedata[j++] = ((trans[0] << 2) & 0xfc) | ((trans[1]>>4) & 0x03);
if (base64[i+2] == '='){
continue;
else{
trans[2] = num_strchr(base64char, base64[i + 2]);
// 2/3
dedata[j++] = ((trans[1] << 4) & 0xf0) | ((trans[2] >> 2) & 0x0f);
if (base64[i + 3] == '=')
continue;
trans[3] = num_strchr(base64char, base64[i + 3]);
// 3/3