如何在HttpClient/MultipartFormDataContent中禁用base64编码的文件名

6
我正在使用HttpClient向Java web应用程序MultipartFormDataContent发送POST请求。我上传了几个StringContents和一个文件,我使用MultipartFormDataContent.Add(HttpContent content, String name, String fileName)方法将其添加为StreamContent,并使用方法HttpClient.PostAsync(String, HttpContent)上传。

这很顺利,除非我提供包含德语umlauts的fileName(我还没有测试其他非ASCII字符)。在这种情况下,fileName被base64编码。文件名为99 2 LD 353 Temp Äüöß-1.txt的结果如下:

看起来像这样:

 __utf-8_B_VGVtcCDvv73vv73vv73vv71cOTkgMiBMRCAzNTMgVGVtcCDvv73vv73vv73vv70tMS50eHQ___

Java服务器在其用户界面中显示了这个编码后的文件名,这让用户感到困惑。我无法进行任何服务器端的更改。

我该如何禁用这种行为呢?非常感谢您提供的任何帮助。

提前致谢!

4个回答

11

我发现了与StrezzOr相同的限制,因为我正在使用的服务器没有遵循filename*标准。

我将文件名转换为UTF-8表示的字节数组,然后重新将字节作为“简单”字符串(非UTF-8)的字符装载。

此代码创建了一个内容流并将其添加到多部分内容中:

        FileStream fs = File.OpenRead(_fullPath);
        StreamContent streamContent = new StreamContent(fs);
        streamContent.Headers.Add("Content-Type", "application/octet-stream");
        String headerValue = "form-data; name=\"Filedata\"; filename=\"" + _Filename + "\"";
        byte[] bytes = Encoding.UTF8.GetBytes(headerValue);
        headerValue="";
        foreach (byte b in bytes)
        {
            headerValue += (Char)b;
        }
        streamContent.Headers.Add("Content-Disposition", headerValue);
        multipart.Add(streamContent, "Filedata", _Filename);

这可以处理带有西班牙口音的内容。

希望这能有所帮助。


2
请注意,不需要使用multipart.Add(streamContent,“Filedata”,_Filename)。multipart.Add(streamContent)就足够了。目前,multipart.Add(streamContent,“Filedata”,_Filename)将跳过最后2个参数,因为标头已经存在,但是在将来可能会更改(它将覆盖\替换而不是跳过)。 - osexpert

2

最近我发现了这个问题,并在此提供一个解决方法:

在服务器端:

private static readonly Regex _regexEncodedFileName = new Regex(@"^=\?utf-8\?B\?([a-zA-Z0-9/+]+={0,2})\?=$");

private static string TryToGetOriginalFileName(string fileNameInput) {
    Match match = _regexEncodedFileName.Match(fileNameInput);
    if (match.Success && match.Groups.Count > 1) {
        string base64 = match.Groups[1].Value;
        try {
            byte[] data = Convert.FromBase64String(base64);
            return Encoding.UTF8.GetString(data);
        }
        catch (Exception) {
            //ignored
            return fileNameInput;
        }
    }
    return fileNameInput;
}

然后像这样使用此函数:
string correctedFileName = TryToGetOriginalFileName(fileRequest.FileName);

它有效。


1
为了在Content-Disposition头文件的文件名属性中传递非ASCII字符,需要使用filename*属性而不是常规的filename。请参见规范here
要使用HttpClient实现此操作,可以执行以下操作,
   var streamcontent = new StreamContent(stream);
   streamcontent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
      FileNameStar = "99 2 LD 353 Temp Äüöß-1.txt" 
   };
   multipartContent.Add(streamcontent);

然后标题将会看起来像这样:

  Content-Disposition: attachment; filename*=utf-8''99%202%20LD%20353%20Temp%20%C3%84%C3%BC%C3%B6%C3%9F-1.txt

很遗憾,服务器似乎不尊重文件名*或者它也被base64编码了,因为在应用程序的UI中,该文件仍然显示为编码名称。由于您的示例标题看起来是URL编码的,我还尝试对文件名进行URL编码,但是Web应用程序会将其原样显示,而不解码。 - Strezz0r

1

最终,我放弃了使用 HttpClient,改用 HttpWebRequest 来解决这个任务。我必须手动构建标头和内容,但这使我能够忽略发送非 ASCII 文件名的标准。最终,我将未编码的 UTF-8 文件名塞入了 filename 标头中,这是服务器接受我的请求的唯一方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接