Idhttp Indy 使用 utf-8 文件名上传到 Dropbox

Idhttp Indy upload to Dropbox with utf-8 file name

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

如何上传文件并保存 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;

对于header中参数的调用,需要对这些字符进行转义。也就是说,当您使用“Dropbox-API-Arg”header 时,您需要使其成为“HTTP header 安全”。这意味着对字符 0x7F 和所有 non-ASCII 个字符使用 JSON-style “\uXXXX” 转义码。

一些,但不是全部,languages/libraries 为您做这件事。例如,对于 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 })

听起来您的平台并没有为您做这些,因此您需要在代码中实现它。

HTTP headers 不能包含 non-ASCII 个字符,甚至不能包含 UTF-8。如果您需要在 HTTP header 中发送 non-ASCII 个字符,它们必须以 ASCII-compatible 格式编码。

在这种情况下,由于问题中的 header 包含 JSON,您可以使用 JSON 自己的 \uXXXX 语法来编码 non-ASCII 个字符:

Any code point may be represented as a hexadecimal number. The meaning of such a number is determined by ISO/IEC 10646. If the code point is in the Basic Multilingual Plane (U+0000 through U+FFFF), then it may be represented as a six-character sequence: a reverse solidus, followed by the lowercase letter u, followed by four hexadecimal digits that encode the code point. Hexadecimal digits can be digits (U+0030 through U+0039) or the hexadecimal letters A through F in uppercase (U+0041 through U+0046) or lowercase (U+0061 through U+0066). So, for example, a string containing only a single reverse solidus character may be represented as "\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;

有很多第 3 方 JSON 库可用于 Delphi,如果您需要支持其他 Delphi 版本。