2017-02-22 20 views
0

WebサービスにPOSTを実装しようとしています。私は、タイプが可変のファイル(.docx.pdf.txt)をJSON形式の文字列とともに送信する必要があります。Indyで投稿し、ファイル名にギリシャ文字が含まれていると、ファイルのアップロードに失敗する

私は、次のようなコードで正常にファイルを投稿するために管理しています

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ヘッダーが、EncodeHeader()機能を使用して、IndyのTIdFormDataFieldクラスによってエンコードされていることがわかりました。ポストが失敗すると、ヘッダー内のエンコードされたファイル名が分割され、分割されていない正常なポストと比較されます。例えば

  • Επιστολή εκπαιδευτικο.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としてエンコードされますが、これは失敗します。

私は、ファイル名のエンコーディングを変更するAddFile()手順のAContentTypeを試みたが、ContentTransferが、それらのどれも動作を変更せず、エンコードされたファイル名が分割されたときに、私はまだエラーを取得しています。

これは何らかのバグですか、それとも不足していますか?

私のコードは、上記以外のすべてのケースで機能します。

私はIndy10でDelphi XE3を使用しています。

答えて

1

EncodeHeader()は、Unicode文字列を持ついくつかの既知の問題を持っている:

EncodeHeader() needs to take codeunits into account when splitting data between adjacent encoded-words

基本的には、MIMEエンコードされた単語はとても長いテキストが別れます、長さが75以上の文字にすることはできません。しかし、長いUnicode文字列をエンコードする場合、Unicode文字は1バイト以上を使用してcharsetエンコードされ、EncodeHeader()は2バイト間のマルチバイト文字を別々のエンコードされた単語に分割することを未だ避けていません(違法で明示的ですMIME仕様のRFC 2047によって禁止されています)。

しかし、これはあなたの例では起こっていません。

最初の例では、'Επιστολή εκπαιδευτικο.docx'が長すぎて単一のMIMEワードとしてエンコードされないため、'Επιστολή εκπαιδευτικο.doc''x'サブストリングに分割され、別々にエンコードされます。 長いテキストの場合、これはMIMEで合法です(ただし、Indyがテキストを'Επιστολή'' εκπαιδευτικο.doc'に分割するか、'Επιστολή'' εκπαιδευτικο''.doc'に分割することが予想されるかもしれません)。それは将来のリリースで可能性があります)。空白のみで区切られた隣接するMIME単語​​は、復号化されたときに空白を区切らずに連結されることを意味し、したがって'Επιστολή εκπαιδευτικο.docx'を再び生成する。サーバーがそれをしていない場合、それはデコーダに瑕疵があります(代わりに、'Επιστολή εκπαιδευτικο.doc x'としてデコードしていますか?)。

2番目の例では、'Επιστολή εκπαιδευτικ.docx'は1つのMIMEワードとしてエンコードできるほど短いです。

あなたの第3の例では、'Επιστολή εκπαιδευτικ .docx''Επιστολή εκπαιδευτικ'' .docx'ストリングに第二の空白(最初せず)に分割されます、そして最初のサブストリングは、符号化される必要があります。 これはMIMEで有効です。デコードされると、デコードされたテキストは次のエンコードされていないテキストと連結され、それらの間の空白が維持され、再び'Επιστολή εκπαιδευτικ .docx'が生成されます。サーバーがそれをしていない場合、それはデコーダに瑕疵があります(代わりに'Επιστολή εκπαιδευτικ.docx'としてデコードされているのでしょうか?)。

あなたはインディの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; 

をので、問題はサーバー側のデコードではなく、インディのクライアント側のエンコーディングであると思われます。あなたはインディ10(2011年11月以降)のごく最近のバージョンを使用している場合は言われていること

は、TIdFormDataFieldは、ユニコード環境で'B'デフォルト(BASE64)HeaderEncoding性質を持っています。しかし、分割ロジックも影響'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'と解釈されます)。

+0

ご回答いただきありがとうございます。私はサーバーの所有者に連絡して、それを一緒にデバッグしようとします。一方、私は2番目の回避策(8ビット)を試して、魅力的に働いた。 – stmpakir

関連する問題