gpt4 book ai didi

delphi - 使用 Indy 发布且文件名包含希腊字符时,文件上传失败

转载 作者:行者123 更新时间:2023-12-03 18:30:15 27 4
gpt4 key购买 nike

我正在尝试实现 POST到网络服务。我需要发送一个类型为变量的文件( .docx.pdf.txt )以及 JSON 格式的字符串。

我已经设法使用类似于以下的代码成功发布文件:

procedure DoRequest;
var
Http: TIdHTTP;
Params: TIdMultipartFormDataStream;
RequestStream, ResponseStream: TStringStream;
JRequest, JResponse: TJSONObject;
url: string;
begin
url := 'some_custom_service'

JRequest := TJSONObject.Create;
JResponse := TJSONObject.Create;
try
JRequest.AddPair('Pair1', 'Value1');
JRequest.AddPair('Pair2', 'Value2');
JRequest.AddPair('Pair3', 'Value3');

Http := TIdHTTP.Create(nil);
ResponseStream := TStringStream.Create;
RequestStream := TStringStream.Create(UTF8Encode(JRequest.ToString));
try
Params := TIdMultipartFormDataStream.Create;
Params.AddFile('File', ceFileName.Text, '').ContentTransfer := '';
Params.AddFormField('Json', 'application/json', '', RequestStream);

Http.Post(url, Params, ResponseStream);
JResponse := TJSONObject.ParseJSONValue(ResponseStream.DataString) as TJSONObject;
finally
RequestStream.Free;
ResponseStream.Free;
Params.Free;
Http.Free;
end;
finally
JRequest.Free;
JResponse.Free;
end;
end;

当我尝试发送文件名中包含希腊字符和空格的文件时出现问题。有时会失败,有时会成功。

经过大量研究,我注意到 POST header 由 Indy 的 TIdFormDataField 编码使用 EncodeHeader() 的类功能。当 post 失败时,header 中的编码文件名会被拆分,而成功的 post 则不会被拆分。

例如 :
  • Επιστολή εκπαιδευτικο.docx被编码为 =?UTF-8?B?zpXPgM65z4PPhM6/zrvOriDOtc66z4DOsc65zrTOtc+Fz4TOuc66zr8uZG9j?='#$D#$A' =?UTF-8?B?eA==?= ,失败了。
  • Επιστολή εκπαιδευτικ.docx被编码为=?UTF-8?B?zpXPgM65z4PPhM6/zrvOriDOtc66z4DOsc65zrTOtc+Fz4TOuc66LmRvY3g=?= , 成功了。
  • Επιστολή εκπαιδευτικ .docx被编码为=?UTF-8?B?zpXPgM65z4PPhM6/zrvOriDOtc66z4DOsc65zrTOtc+Fz4TOuc66?= .docx ,失败了。

  • 我试图更改文件名的编码 AContentTypeAddFile()程序和 ContentTransfer ,但是这些都不会改变行为,并且在拆分编码的文件名时仍然会出错。

    这是某种错误,还是我错过了什么?

    我的代码适用于所有情况,除了我上面描述的那些。

    我正在使用带有 Indy10 的 Delphi XE3。

    最佳答案

    EncodeHeader() Unicode 字符串确实存在一些已知问题:
    EncodeHeader() needs to take codeunits into account when splitting data between adjacent encoded-words
    基本上,一个 MIME 编码的单词长度不能超过 75 个字符,因此长文本会被拆分。但是在对长 Unicode 字符串进行编码时,任何给定的 Unicode 字符都可以使用 1 个或更多字节进行字符集编码,并且 EncodeHeader()尚未避免错误地将两个单独字节之间的多字节字符拆分为单独的编码字(这是非法的,并且被 MIME 规范的 RFC 2047 明确禁止)。
    但是,这不是您的示例中发生的情况。
    在您的第一个示例中,'Επιστολή εκπαιδευτικο.docx'太长而无法编码为单个 MIME 字,因此它被拆分为 'Επιστολή εκπαιδευτικο.doc' 'x'子串,然后分别编码。 这在 MIME 中对于长文本是合法的 (尽管您可能希望 Indy 将文本拆分为 'Επιστολή' ' εκπαιδευτικο.doc' ,甚至 'Επιστολή' ' εκπαιδευτικο' '.doc' 。这可能会在 future 的版本中出现)。仅由空格分隔的相邻 MIME 词意味着在解码时连接在一起而不分隔空格,从而产生 'Επιστολή εκπαιδευτικο.docx'再次。如果服务器不这样做,则它的解码器存在缺陷(也许它正在解码为 'Επιστολή εκπαιδευτικο.doc x'?)。
    在您的第二个示例中,'Επιστολή εκπαιδευτικ.docx'足够短,可以编码为单个 MIME 字。
    在您的第三个示例中,'Επιστολή εκπαιδευτικ .docx'在第二个空格(不是第一个)上被拆分为 'Επιστολή εκπαιδευτικ' ' .docx'子字符串,并且只需要对第一个子字符串进行编码。 这在 MIME 中是合法的 .解码时,解码后的文本将与以下未编码的文本连接,在它们之间保留空格,从而产生 'Επιστολή εκπαιδευτικ .docx'再次。如果服务器不这样做,则它的解码器存在缺陷(也许它正在解码为 'Επιστολή εκπαιδευτικ.docx'?)。
    如果您通过 Indy 的 MIME header 编码器/解码器运行这些示例文件名,它们会正确解码:

    var
    s: String;
    begin
    s := EncodeHeader('Επιστολή εκπαιδευτικο.docx', '', 'B', 'UTF-8');
    ShowMessage(s); // '=?UTF-8?B?zpXPgM65z4PPhM6/zrvOriDOtc66z4DOsc65zrTOtc+Fz4TOuc66zr8uZG9j?='#13#10' =?UTF-8?B?eA==?='
    s := DecodeHeader(s);
    ShowMessage(s); // 'Επιστολή εκπαιδευτικο.docx'

    s := EncodeHeader('Επιστολή εκπαιδευτικ.docx', '', 'B', 'UTF-8');
    ShowMessage(s); // '=?UTF-8?B?zpXPgM65z4PPhM6/zrvOriDOtc66z4DOsc65zrTOtc+Fz4TOuc66LmRvY3g=?='
    s := DecodeHeader(s);
    ShowMessage(s); // 'Επιστολή εκπαιδευτικ.docx'

    s := EncodeHeader('Επιστολή εκπαιδευτικ .docx', '', 'B', 'UTF-8');
    ShowMessage(s); // '=?UTF-8?B?zpXPgM65z4PPhM6/zrvOriDOtc66z4DOsc65zrTOtc+Fz4TOuc66?= .docx'
    s := DecodeHeader(s);
    ShowMessage(s); // 'Επιστολή εκπαιδευτικ .docx'
    end;
    所以问题似乎出在服务器端解码上,而不是在 Indy 的客户端编码上。
    话虽如此,如果您使用的是相当新的 Indy 10 版本(2011 年 11 月或更高版本), TIdFormDataField有一个 HeaderEncoding属性,默认为 'B' (base64) 在 Unicode 环境中。但是,拆分逻辑也会影响 'Q' (quoted-printable) 也是如此,所以这可能对你有用,也可能对你不起作用(但你可以试试):
    with Params.AddFile('File', ceFileName.Text, '') do
    begin
    ContentTransfer := '';
    HeaderEncoding := 'Q'; // <--- here
    HeaderCharSet := 'utf-8';
    end;
    否则,解决方法可能是将值更改为 '8' (8 位),它有效地禁用 MIME 编码(但不是字符集编码):
    with Params.AddFile('File', ceFileName.Text, '') do
    begin
    ContentTransfer := '';
    HeaderEncoding := '8'; // <--- here
    HeaderCharSet := 'utf-8';
    end;
    请注意,如果服务器不希望文件名使用原始 UTF-8 字节,您可能仍然会遇到问题(例如, 'Επιστολή εκπαιδευτικο.docx' 被解释为 'Επιστολή εκπαιδευτικο.docx')。

    关于delphi - 使用 Indy 发布且文件名包含希腊字符时,文件上传失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42390870/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com