【问题标题】:Idhttp Indy upload to Dropbox with utf-8 file nameIdhttp Indy 使用 utf-8 文件名上传到 Dropbox
【发布时间】:2017-01-19 21:52:25
【问题描述】:

我正在尝试使用 Indy 10 和 XE8 将文件上传到 Dropbox。虽然像'file.txt'这样的文件名可以正常工作,但使用'файл.txt'左右,我在DropBox上有'????.txt'。我阅读了有关 utf-8 的参数,但它不适用于标头:-(。

如何上传文件并保存 utf-8 文件名?

procedure TSaveFilterForm.TestButtonClick(Sender: TObject);
const
  URL = 'https://content.dropboxapi.com/2/files/upload';
var
  IdHTTP: TIdHTTP;
  Source: TFileStream;
  Res: String;
begin                           
  IdHTTP := TIdHTTP.Create(nil);
  try
    IdHTTP.HandleRedirects := True;
    IdHTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
    IdHTTP.Request.BasicAuthentication := False;
    IdHTTP.Request.CustomHeaders.Values['Authorization'] := 'Bearer ' + AccessToken;
    IdHTTP.Request.ContentType := 'application/octet-stream';
    Source := TFileStream.Create('c:\test.txt', fmOpenRead);

    IdHTTP.Request.CustomHeaders.Values['Dropbox-API-Arg'] :=
      Format('{ "path": "%s", "mode": "overwrite"}',
        ['/файл.txt']); // Dropbox => ????.txt
    try
      Res := IdHTTP.Post(URL, Source);
    finally
      Source.Free;
    end;
  finally
    IdHTTP.Free;
  end;
end;

【问题讨论】:

    标签: delphi dropbox indy delphi-2009 indy10


    【解决方案1】:

    HTTP 标头不能包含非 ASCII 字符,即使是 UTF-8。如果您需要在 HTTP 标头中发送非 ASCII 字符,则必须以兼容 ASCII 的格式对其进行编码。

    在这种情况下,由于相关标头包含 JSON,您可以使用 JSON 自己的 \uXXXX 语法对非 ASCII 字符进行编码:

    任何代码点都可以表示为十六进制数。此类数字的含义由 ISO/IEC 10646 确定。如果代码点位于基本多语言平面(U+0000 到 U+FFFF),则它可以表示为六个字符的序列:反斜线,后跟小写字母 u,后跟四个编码代码点的十六进制数字。十六进制数字可以是数字(U+0030 到 U+0039)或大写(U+0041 到 U+0046)或小写(U+0061 到 U+0066)的十六进制字母 A 到 F。因此,例如,一个只包含一个反斜线字符的字符串可以表示为“\u005C”。

    例如:

    procedure TSaveFilterForm.TestButtonClick(Sender: TObject);
    const
      URL = 'https://content.dropboxapi.com/2/files/upload';
    var
      IdHTTP: TIdHTTP;
      Source: TFileStream;
      Res: String;
    begin                           
      IdHTTP := TIdHTTP.Create(nil);
      try
        IdHTTP.HandleRedirects := True;
        IdHTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
        IdHTTP.Request.BasicAuthentication := False;
        IdHTTP.Request.CustomHeaders.Values['Authorization'] := 'Bearer ' + AccessToken;
        IdHTTP.Request.ContentType := 'application/octet-stream';
    
        IdHTTP.Request.CustomHeaders.Values['Dropbox-API-Arg'] := Format(
          '{ "path": "%s", "mode": "overwrite"}',
          ['/\u0444\u0430\u0439\u043B.txt']
        );
    
        Source := TFileStream.Create('c:\test.txt', fmOpenRead);
        try
          Res := IdHTTP.Post(URL, Source);
          // alternatively, without using TFileStream manually:
          // Res := IdHTTP.Post(URL, 'C:\test.txt');
        finally
          Source.Free;
        end;
      finally
        IdHTTP.Free;
      end;
    end;
    

    我建议您使用 JSON 库来创建 JSON 内容。例如,您可以在 Delphi 2010 及更高版本中使用 Delphi 自己的JSON framework,例如:

    uses
      ...,
      // use DBXJSON in D2010-XE
      // use Data.DBXJSON in XE2-XE5
      // use System.JSON in XE6+
      ;
    
    procedure TSaveFilterForm.TestButtonClick(Sender: TObject);
    const
      URL = 'https://content.dropboxapi.com/2/files/upload';
    var
      IdHTTP: TIdHTTP;
      Json: TJSONObject;
      Res: String;
    begin                           
      IdHTTP := TIdHTTP.Create(nil);
      try
        IdHTTP.HandleRedirects := True;
        IdHTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
        IdHTTP.Request.BasicAuthentication := False;
        IdHTTP.Request.CustomHeaders.Values['Authorization'] := 'Bearer ' + AccessToken;
        IdHTTP.Request.ContentType := 'application/octet-stream';
    
        Json := TJSONObject.Create;
        try
          Json.AddPair('path', '/файл.txt');
          Json.AddPair('mode', 'overwrite');
          IdHTTP.Request.CustomHeaders.Values['Dropbox-API-Arg'] := Json.ToJSON;
        finally
          Json.Free;
        end;
    
        Res := IdHTTP.Post(URL, 'C:\test.txt');
      finally
        IdHTTP.Free;
      end;
    end;
    

    如果您需要支持其他 Delphi 版本,则有大量可用于 Delphi 的 3rd 方 JSON 库。

    【讨论】:

    • 谢谢。使用 \uXXXX 它工作正常。我没有尝试 TJSONObject,因为 D2009 没有。
    • @OlegAdibekov 有很多支持 2009 的 Delphi 的第三方 JSON 库。
    • 我使用 LkJSON,@RemyLebeau。但在这种情况下,$IFDEF 将太多。 1.DXE6++ JSON; 2. D2010+ + DBXJSON; 3. D2009 (utf-8) + LkJSON; 4. 旧(Ansi)Delphi + LkJSON。太复杂了。
    • @OlegAdibekov:或者,您可以选择一个适用于所有版本的 JSON 库。毕竟,LkJSON 可以在比 D2009 更新的版本中使用。您不必必须使用 Embarcadero 的 JSON 库。
    • 我尝试了 LkJSON,它在 XE8 上运行良好(在更改 String -> AnsiString 等之后)。但是 JSON 放在规范中。
    【解决方案2】:

    对于这些在标头中带有参数的调用,您需要对这些字符进行转义。也就是说,当您使用“Dropbox-API-Arg”标头时,您需要将其设为“HTTP 标头安全”。这意味着对字符 0x7F 和所有非 ASCII 字符使用 JSON 样式的“\uXXXX”转义码。

    某些(但不是全部)语言/库会为您执行此操作。例如,对于 JavaScript,要自己执行此操作,您可以执行以下操作:

    var charsToEncode = /[\u007f-\uffff]/g;
    
    function http_header_safe_json(v) {
     return JSON.stringify(v).replace(charsToEncode,
     function(c) {
     return '\\u' + ('000' + c.charCodeAt(0).toString(16)).slice(-4);
     }
     );
    }
    

    然后是这样的:

    'Dropbox-API-Arg': http_header_safe_json({ path: dropboxFilePath })
    

    听起来你的平台不适合你,所以你需要在你的代码中实现它。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-21
    相关资源
    最近更新 更多