0

我正在使用 TAmazonStorageService 类 UploadObject 方法将 JSON 字符串放入 Amazon S3。当我检索对象时,它被放置在一个流中(我使用的是 TStringStream),它似乎是用 UTF-16 LE 编码的。然后,如果我尝试将该 JSON 加载到备忘录、TStringList 或任何其他类似对象中,我只会得到第一个字符,即 JSON 的左花括号。另一方面,如果我将它写入一个文件,我会得到整个 JSON(UTF-16 LE 编码)。我假设因为 UTF-16 LE 用两个字节对每个字符进行编码,并且第二个字节始终为 0,Delphi 假设 0 是文件结束标记。

如何从 TStringStream 中获取常规的 Delphi 字符串 (WideString),甚至是 ANSIString,或者是否有另一个流可以用来获取 WideString 或 ANSIString。

这是代表上传的伪代码:

procedure StorePayload( AmazonConnectionInfo: TAmazonConnectionInfo; JSONString: String;
                        PayloadMemTable: TFDAdaptedDataSet;
                        PayloadType: String; PayloadVersion: Integer);
var
  AmazonStorageService: TAmazonStorageService;
  ab: TBytes;
  ResponseInfo: TCloudResponseInfo;
  ss: TStringStream;
  Guid: TGuid;
begin
  Guid := TGuid.NewGuid;
  AmazonStorageService := TAmazonStorageService.Create( AmazonConnectionInfo );
  try
  // Write payload to S3
  ResponseInfo := TCloudResponseInfo.Create;
  try
    ss := TStringStream.Create( JSONString );
    try
      ab := StringToBytes( ss.DataString );
      if AmazonStorageService.UploadObject( BucketName, Guid.ToString, ab, false, nil, nil, amzbaPrivate, ResponseInfo ) then
        PayloadMemTable.AppendRecord( [Guid.ToString, PayloadType, PayloadVersion, now() ] );
    finally
      ss.Free;
    end;
  finally
    ResponseInfo.Free;
  end;
  finally
    AmazonStorageService.Free;
  end;
end;

这是代表 JSON 检索的伪代码:

function RetrievePayload( AmazonConnectionInfo: TAmazonConnectionInfo ): String;
var
  AmazonStorageService: TAmazonStorageService;
  ObjectName: string;
  ResponseInfo: TCloudResponseInfo;
  ss: TStringStream;
  OptParams: TAmazonGetObjectOptionals;
begin
  // I tried with and without the TAmazonGetObjectOptionals
  OptParams := TAmazonGetObjectOptionals.Create;
  OptParams.ResponseContentEncoding := 'ANSI';
  OptParams.ResponseContentType := 'text/plain';
  AmazonStorageService := TAmazonStorageService.Create( AmazonConnectionInfo );
  try
    ss := TStringStream.Create( );
    try
      ResponseInfo := TCloudResponseInfo.Create;
      try
        if not AmazonStorageService.GetObject( BucketName, PayloadID, OptParams, 
                                               ss, ResponseInfo, amzrNotSpecified ) then
          raise Exception.Create('Error retrieving item ' + ObjectName);
      Result := ss.DataString;
      // The memo will contain only {
      Form1.Memo1.Lines.Text := ss.DataString;
      finally
        ResponseInfo.Free;
      end;
    finally
      ss.Free;
    end;
  finally
    AmazonStorageService.Free;
  end;
end;
4

1 回答 1

3

在 Delphi 2009 及更高版本中,String是 UTF-16 UnicodeString,但TStringStream默认在 8 位 ANSI 上运行(为了向后兼容 Unicode 之前的 Delphi 版本)。

完全不需要StorePayload()使用TStringStream。您将 a 存储String到流中只是为了从中读取一个String回退。因此,只需按原样使用原始String文件。

StringToBytes()也不需要使用。您可以而且应该改用TEncoding.UTF8UTF-8,因为 UTF-8 是 JSON 数据的首选编码,例如:

procedure StorePayload( AmazonConnectionInfo: TAmazonConnectionInfo; JSONString: String;
                        PayloadMemTable: TFDAdaptedDataSet;
                        PayloadType: String; PayloadVersion: Integer);
var
  AmazonStorageService: TAmazonStorageService;
  ab: TBytes;
  ResponseInfo: TCloudResponseInfo;
  Guid: TGuid;
begin
  Guid := TGuid.NewGuid;
  AmazonStorageService := TAmazonStorageService.Create( AmazonConnectionInfo );
  try
    // Write payload to S3
    ResponseInfo := TCloudResponseInfo.Create;
    try
      ab := TEncoding.UTF8.GetBytes( JSONString );
      if AmazonStorageService.UploadObject( BucketName, Guid.ToString, ab, false, nil, nil, amzbaPrivate, ResponseInfo ) then
        PayloadMemTable.AppendRecord( [Guid.ToString, PayloadType, PayloadVersion, Now() ] );
    finally
      ResponseInfo.Free;
    end;
  finally
    AmazonStorageService.Free;
  end;
end;

相反,当稍后RetrievePayload()调用时GetObject(),您可以使用TEncoding.UTF8withTStringStream来解码String,例如:

function RetrievePayload( AmazonConnectionInfo: TAmazonConnectionInfo ): String;
var
  AmazonStorageService: TAmazonStorageService;
  ResponseInfo: TCloudResponseInfo;
  ss: TStringStream;
begin
  AmazonStorageService := TAmazonStorageService.Create( AmazonConnectionInfo );
  try
    ss := TStringStream.Create( '', TEncoding.UTF8 );
    try
      ResponseInfo := TCloudResponseInfo.Create;
      try
        if not AmazonStorageService.GetObject( BucketName, PayloadID, ss, ResponseInfo, amzrNotSpecified ) then
          raise Exception.Create('Error retrieving item ' + ObjectName);
        Result := ss.DataString;
        Form1.Memo1.Text := Result;
      finally
        ResponseInfo.Free;
      end;
    finally
      ss.Free;
    end;
  finally
    AmazonStorageService.Free;
  end;
end;

如果您需要检索已上传为 UTF-16 的任何预先存在的存储桶对象,RetrievePayload()可以使用TEncoding.Unicode

ss := TStringStream.Create( '', TEncoding.Unicode );

但是,这不适用于使用 UTF-8 上传的较新对象。因此,更灵活的解决方案是使用TMemoryStreamor检索原始字节TBytesStream,然后分析字节以确定使用的是 UTF8 还是 UTF-16,然后使用TEncoding.UTF8.GetString()orTEncoding.Unicode.GetString()将字节解码为String.

于 2020-05-18T00:19:45.413 回答