libcurl中上传文件的坑-英文操作系统中文路径无法上传返回26错误码
最近在使用libcurl使用表单形式上传本地文件,自己测试的时候都没有问题,但是在测试的电脑上无法上传,返回26错误码,在libcurl的错误码里表示无法读取文件。我把测试上的文件名字改成全英文发现可以上传,由于测试电脑是英文操作系统,所以应该是英文系统中需要上传的文件包含中文路径,这个路径传给libcurl无法找到对应的文件,所以上传失败。
传给liburl的路径是ansi格式的,在网上搜索相关编码问题,得出结论,ansi编码在英文操作系统中表示ascii编码,在中文操作系统中表示gbk编码,在韩文和日文操作系统表示什么大家也就很清楚了。ansi在各个语言的操作系统表示对应各个语言国家规定的文字编码标准。所以,在英文操作系统中使用ansi编码是无法表示中文的,所以在英文操作系统中如果有汉字的unicode字符串转换成ansi就必定会出错,转换后的ansi编码也无法再转换回unicode。
确定问题原因后,开始找解决方案,既然英文操作系统无法使用带中文的ansi,而libcurl上传文件又只能接收ansi编码的路径,所以以我的智商,只能想到两个解决方案:1 传入全英文路径,也就是把文件先复制成英文传过去再上传;2 传入utf8格式路径代替ansi,打开文件时再转成unicode方式打开文件。程序员都知道肯定是第二个效率高,所以决定用第二种方式,修改libcurl中的源代码,把上传过程中对文件路径的操作都改成宽字节方式,即uncode方式。问题解决。其实大神说还可以用libcurl中的read_callback方式,只是我没有深入研究。
贴上修改代码供参考,我们用的是7.68版本的源代码,只修改其中的mine.c文件。
/* Set mime part content from named local file. */
CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
{
CURLcode result = CURLE_OK;
if(!part)
return CURLE_BAD_FUNCTION_ARGUMENT;
cleanup_part_content(part);
if(filename) {
char *base;
struct_stat sbuf;
if (!stat(filename, &sbuf) && !access(filename, R_OK)) {
part->data = strdup(filename);
if (!part->data)
result = CURLE_OUT_OF_MEMORY;
part->datasize = -1;
if (!result && S_ISREG(sbuf.st_mode)) {
part->datasize = filesize(filename, sbuf);
part->seekfunc = mime_file_seek;
}
part->readfunc = mime_file_read;
part->freefunc = mime_file_free;
part->kind = MIMEKIND_FILE;
/* As a side effect, set the filename to the current file’s base name.
It is possible to withdraw this by explicitly calling
curl_mime_filename() with a NULL filename argument after the current
call. */
base = strippath(filename);
if (!base)
result = CURLE_OUT_OF_MEMORY;
else {
CURLcode res = curl_mime_filename(part, base);
if (res)
result = res;
free(base);
}
}
else {
//try again with unicode
struct _stat64i32 file_stat;
wchar_t* wfilename = get_unicode_from_utf8(filename);
if (_wstat(wfilename, &file_stat) || _waccess(wfilename, R_OK))
result = CURLE_READ_ERROR;
part->data = strdup(filename);
if (!part->data)
result = CURLE_OUT_OF_MEMORY;
part->datasize = -1;
if (!result && S_ISREG(file_stat.st_mode)) {
part->datasize = file_stat.st_size;// filesize(wfilename, sbuf);
part->seekfunc = mime_file_seek;
}
part->readfunc = mime_file_read;
part->freefunc = mime_file_free;
part->kind = MIMEKIND_FILE;
/* As a side effect, set the filename to the current file’s base name.
It is possible to withdraw this by explicitly calling
curl_mime_filename() with a NULL filename argument after the current
call. */
base = strippath(filename);
if (!base)
result = CURLE_OUT_OF_MEMORY;
else {
CURLcode res = curl_mime_filename(part, base);
if (res)
result = res;
free(base);
}
free(wfilename);
}
}
return result;
}
/* Named file callbacks. */
/* Argument is a pointer to the mime part. */
static int mime_open_file(curl_mimepart * part)
{
/* Open a MIMEKIND_FILE part. */
if(part->fp)
return 0;
part->fp = fopen_read(part->data, “rb”);
if (!part->fp) {
//Open file failed, convert path to unicode from ansi and try to reopen with _wfopen again
wchar_t * unicode = get_unicode_from_utf8(part->data);
part->fp = _wfopen(unicode, L”rb”);
free(unicode);
}
return part->fp? 0: -1;
}