簡體   English   中英

文件上傳失敗,當使用 Indy 發布並且文件名包含希臘字符時

[英]File upload fails, when posting with Indy and filename contains Greek characters

我正在嘗試對 Web 服務實施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標頭是由 Indy 的TIdFormDataField類使用EncodeHeader()函數編碼的。 當發布失敗時,與未拆分的成功發布相比,標頭中的編碼文件名被拆分。

例如 :

  • Επιστολή εκπαιδευτικο.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 ,這失敗了。

我曾試圖改變文件名的編碼,該AContentType中的AddFile()方法,以及ContentTransfer我,但沒有這些改變的行為,並仍然出現錯誤時編碼的文件名是分裂的。

這是某種錯誤,還是我錯過了什么?

我的代碼適用於除我上面描述的那些情況之外的所有情況。

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

EncodeHeader()確實有一些 Unicode 字符串的已知問題:

EncodeHeader() 在相​​鄰編碼字之間拆分數據時需要考慮代碼單元

基本上,一個 MIME 編碼的單詞長度不能超過 75 個字符,因此長文本會被拆分。 但是在對長 Unicode 字符串進行編碼時,任何給定的 Unicode 字符都可能使用 1 個或更多字節進行字符集編碼,並且EncodeHeader()還不能避免錯誤地將兩個單獨字節之間的多字節字符拆分為單獨的編碼字(這是非法的)並被 MIME 規范的RFC 2047明確禁止)。

但是,這不是您的示例中發生的情況。

在您的第一個示例中, 'Επιστολή εκπαιδευτικο.docx'太長而無法編碼為單個 MIME 單詞,因此它被拆分為'Επιστολή εκπαιδευτικο.doc' 'x'子字符串,然后單獨編碼。 這在 MIME 中對於長文本是合法的(盡管您可能希望 Indy 將文本拆分為'Επιστολή' ' εκπαιδευτικο.doc' ,或者甚至是'Επιστολή' ' εκπαιδευτικο' '.doc' 。這可能是一種可能性。未來版本)。 僅由空格分隔的相鄰 MIME 詞意味着在解碼時將連接在一起而不分隔空格,從而再次產生'Επιστολή εκπαιδευτικο.docx' 如果服務器沒有這樣做,則它的解碼器存在缺陷(也許它正在解碼為'Επιστολή εκπαιδευτικο.doc x' ?)。

在您的第二個示例中, 'Επιστολή εκπαιδευτικ.docx'足夠短,可以編碼為單個 MIME 單詞。

在你的第三個例子中, 'Επιστολή εκπαιδευτικ .docx'在第二個空格(不是第一個)上'Επιστολή εκπαιδευτικ'分為'Επιστολή εκπαιδευτικ' ' .docx'子字符串,並且只需要對第一個子字符串進行編碼。 這在 MIME 中是合法的 解碼時,解碼文本旨在與以下未編碼文本連接,保留它們之間的空白,從而再次生成'Επιστολή εκπαιδευτικ .docx' 如果服務器沒有這樣做,則它的解碼器存在缺陷(也許它正在解碼為'Επιστολή εκπαιδευτικ.docx' ?)。

如果您通過 Indy 的 MIME 標頭編碼器/解碼器運行這些示例文件名,它們會正確解碼:

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屬性,該屬性在 Unicode 環境中默認為'B' (base64)。 但是,拆分邏輯也會影響'Q' (引用可打印),因此這可能對您有用,也可能對您不起作用(但您可以嘗試):

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' )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM