简体   繁体   中英

Idhttp Indy upload to Dropbox with utf-8 file name

I'm trying to upload a file to Dropbox using Indy 10 and XE8. While file name like 'file.txt' it works OK, but with 'файл.txt' or so, I have '????.txt' on DropBox. I read about parameters with utf-8 but it is don't work with headers :-(.

How can I upload a file and save utf-8 file name?

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;

HTTP headers cannot contain non-ASCII characters, not even UTF-8. If you need to send non-ASCII characters in an HTTP header, they must be encoded in an ASCII-compatible format.

In this case, since the header in question contains JSON, you can use JSON's own \\uXXXX syntax for encoding non-ASCII characters:

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 " \\ ".

For example:

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;

I suggest you use a JSON library to create the JSON content. For instance, you can use Delphi's own JSON framework in Delphi 2010 and later, eg:

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;

There are plenty of 3rd party JSON libraries available for Delphi, if you need to support other Delphi versions.

For these calls with the parameters in the header, you need to escape these characters. That is, when you use the “Dropbox-API-Arg” header, you need to make it “HTTP header safe”. This means using JSON-style “\\uXXXX” escape codes for the character 0x7F and all non-ASCII characters.

Some, but not all, languages/libraries do this for you. For example, for JavaScript, to do this yourself you could do something like this:

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);
 }
 );
}

and then something like:

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

It sounds like your platform doesn't do that for you, so you'll want to implement this in your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM