上传到跳过 Web 服务器

Upload to Skip Web Server

我正在尝试将 JSON 文件上传到 SKIP (getskip.com) 网络服务器,但出现套接字错误 10054。他们不太擅长提供示例,因为他们认为每个人都在使用 cUrl,但我们仍在使用 Delphi XE6 和 Indy。

这是他们提供的。

Java 使用 OK HTTP:

OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
RequestBody body = RequestBody.create(mediaType, "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"path\"; filename=\"<<file_name>>\"\r\nContent-Type: application/vnd.novadigm.ext\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"data\"\r\n\r\n[{\"file_key\": \"path\", \"store_id\"::<<store_id>>}]\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--");
Request request = new Request.Builder()
  .url("https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json")
  .post(body)
  .addHeader("content-type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW")
  .addHeader("Authorization", "Bearer <<api_user_token>>")
  .addHeader("Content-Type", "application/x-www-form-urlencoded")
  .addHeader("User-Agent", "PostmanRuntime/7.13.0")
  .addHeader("Accept", "*/*")
  .addHeader("Cache-Control", "no-cache")
  .addHeader("Postman-Token", "de9932d2-bda0-4df6-8a9a-e2d4f74c2048,d59a9861-1a65-471a-8ada-6e025e985dca")
  .addHeader("Host", "upload.goskip.com:8080")
  .addHeader("accept-encoding", "gzip, deflate")
  .addHeader("content-length", "407")
  .addHeader("Connection", "keep-alive")
  .addHeader("cache-control", "no-cache")
  .build();

Response response = client.newCall(request).execute();

Java 使用 Unirest:

HttpResponse<String> response = Unirest.post("https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json")
  .header("content-type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW")
  .header("Authorization", "Bearer <<api_user_token>>")
  .header("Content-Type", "application/x-www-form-urlencoded")
  .header("User-Agent", "PostmanRuntime/7.13.0")
  .header("Accept", "*/*")
  .header("Cache-Control", "no-cache")
  .header("Postman-Token", "de9932d2-bda0-4df6-8a9a-e2d4f74c2048,d1ea454e-594c-4746-9de7-e813377ff095")
  .header("Host", "upload.goskip.com:8080")
  .header("accept-encoding", "gzip, deflate")
  .header("content-length", "407")
  .header("Connection", "keep-alive")
  .header("cache-control", "no-cache")
  .body("------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"path\"; filename=\"<<file_name>>\"\r\nContent-Type: application/vnd.novadigm.ext\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"data\"\r\n\r\n[{\"file_key\": \"path\", \"store_id\":<<store_id>>}]\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--")
  .asString();

PHP 使用 HttpRequest:

<?php

$request = new HttpRequest();
$request->setUrl('https://upload.goskip.com:8080/v2/backoffice/files/pricebook');
$request->setMethod(HTTP_METH_POST);

$request->setQueryData(array(
  'type' => 'json'
));

$request->setHeaders(array(
  'cache-control' => 'no-cache',
  'Connection' => 'keep-alive',
  'content-length' => '407',
  'accept-encoding' => 'gzip, deflate',
  'Host' => 'upload.goskip.com:8080',
  'Postman-Token' => 'de9932d2-bda0-4df6-8a9a-e2d4f74c2048,c7acbc0a-6450-43d1-b4ca-bc2a56cebc4e',
  'Cache-Control' => 'no-cache',
  'Accept' => '*/*',
  'User-Agent' => 'PostmanRuntime/7.13.0',
  'Content-Type' => 'application/x-www-form-urlencoded',
  'Authorization' => 'Bearer <<api_user_token>>',
  'content-type' => 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
));

$request->setBody('------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="path"; filename="<<file_name>>"
Content-Type: application/vnd.novadigm.ext


------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="data"

[{"file_key": "path", "store_id"::<<store_id>>}]
------WebKitFormBoundary7MA4YWxkTrZu0gW--');

try {
  $response = $request->send();

  echo $response->getBody();
} catch (HttpException $ex) {
  echo $ex;
}

这是我在 Indy 中尝试过的。我将 JSON 文件粘贴到表格的备忘录中:

function TFormJsonWrite.HTTPPost: String;
var
  JsonToSend : TStringStream;
  Response: String;
  IdHTTP1: TIdHTTP;
  IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
begin
  IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create;
  IdHTTP1 := TIdHTTP.Create;
  IdHTTP1.Request.CharSet := 'utf-8';

  JsonToSend := TStringStream.Create(memolog.text, TEncoding.UTF8);
  IdHTTP1.Request.ContentDisposition := 'form-data; name=[{"file_key":''' + memolog.text + ''', "store_id"::001}]------WebKitFormBoundary7MA4YWxkTrZu0gW--';
  IdHTTP1.Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';
  IdHTTP1.Request.ContentType := 'multipart/form-data';
  IdHTTP1.Request.Accept := '*/*';
  IdHTTP1.Request.AcceptEncoding := 'gzip,deflate';
  IdHTTP1.Request.ContentLength := -1;
  IdHTTP1.Request.CacheControl := 'no-cache';
  IdHTTP1.Request.Connection := 'keep-alive';
  IdHTTP1.Request.BasicAuthentication := false;
  IdHTTP1.Request.Host := 'upload.goskip.com:8080';
  IdHTTP1.Request.CustomHeaders.Clear;
  IdHTTP1.Request.CustomHeaders.Values['Authorization'] := 'Bearer eyJhbGc.......................';
  IdHTTP1.Request.CustomHeaders.Values['Postman-Token'] := 'de9932d2-bda0-4df6-8a9a-e2d4f74c2048,d59a9861-1a65-471a-8ada-6e025e985dca';
  IdHTTP1.ReadTimeout := 50000;
  IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvTLSv1_2;
  IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;
  Response := IdHTTP1.Post('https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json', JsonToSend);
  HTTPPost := Response;
  JsonToSend.Free;
  IdHTTP1.Free;
end;

卷曲

Kim@KIMNEW MINGW64 ~
$ curl -F "file=@"C:/mydata/Items-114946.json -H "authorization:Bearer eyJ........................." https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json&store_id=001
[1] 13296
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
Kim@KIMNEW MINGW64 ~
100  951k  100    53  100  951k     53   968k  0:00:01 --:--:--  0:00:01  967k{"message":"#/data: null value where array expected"}

他们更新了所有端点,这是我得到的最新消息。

curl -X POST   'https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json'   -H 'Authorization: Bearer <api_user_token>' -F path=@<path_to_first_file>   -F path1=@<path_to_second_file>   -F 'data=[{"file_key":"path","store_id":<store_id_for_first_file>},{"file_key":"path1","store_id":<store_id_for_second_file>}]'

你的 Delphi 代码全错了。

您将错误的 Content-Disposition header 设置为错误的值。

您发布的 JSON 数据 as-is 根本没有将其包装在 MIME 中。

请勿手动设置 TIdHTTP.Request.AcceptEncoding 属性。您没有设置 TIdHTTP 来启用压缩支持,但您授予服务器发送压缩响应的权限,TIdHTTP 将无法为您解压缩。

使用 TIdHTTP 发送 multipart/form-data 请求的 正确 方法是使用重载的 Post() 方法,该方法采用 TIdMultipartFormDataStream 作为输入,像这样:

function TFormJsonWrite.HTTPPost: String;
var
  JsonToSend : String;
  IdHTTP1: TIdHTTP;
  IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
  PostData: TIdMultipartFormDataStream;
begin
  JsonToSend := '[{"file_key": "path", "store_id": <<store_id>>}]';

  IdHTTP1 := TIdHTTP.Create;
  try
    IdHTTP1.ReadTimeout := 50000;

    IdHTTP1.Request.BasicAuthentication := false;
    IdHTTP1.Request.CustomHeaders.Values['Authorization'] := 'Bearer ...';
    IdHTTP1.Request.CustomHeaders.Values['Postman-Token'] := '...';

    IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
    IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvTLSv1_2;
    IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;

    PostData := TIdMultipartFormDataStream.Create;
    try
      PostData.AddFormField('path', '', '', 'application/vnd.novadigm.ext').FileName := '<<file_name>>';
      PostData.AddFormField('data', JsonToSend, 'utf-8', 'application/json').ContentTransfer := '8bit';

      Result := IdHTTP1.Post('https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json', PostData);
    finally
      PostData.Free;
    end;
  finally
    IdHTTP1.Free;
  end;
end;

如果服务器碰巧 TIdMultipartFormDataStream 格式化 MIME 数据的方式有问题(即,如果服务器拒绝 MIME Content-Type and/or Content-Transfer-Encoding header 由 TIdMultipartFormDataStream 为每个 MIME 字段生成,因为它还不符合 RFC 7578,HTML5 使用),您可以手动格式化 MIME 数据精确匹配 SKIP 的例子,像这样:

function TFormJsonWrite.HTTPPost: String;
var
  JsonToSend : String;
  IdHTTP1: TIdHTTP;
  IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
  PostData: TStringStream;
begin
  JsonToSend := '[{"file_key": "path", "store_id": <<store_id>>}]';

  IdHTTP1 := TIdHTTP.Create;
  try
    IdHTTP1.ReadTimeout := 50000;

    IdHTTP1.Request.ContentType := 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW';
    IdHTTP1.Request.BasicAuthentication := false;
    IdHTTP1.Request.CustomHeaders.Values['Authorization'] := 'Bearer ...';
    IdHTTP1.Request.CustomHeaders.Values['Postman-Token'] := '...';

    IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
    IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvTLSv1_2;
    IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;

    PostData := TStringStream.Create(
      '------WebKitFormBoundary7MA4YWxkTrZu0gW' + EOL +
      'Content-Disposition: form-data; name="path"; filename="<<file_name>>"' + EOL +
      'Content-Type: application/vnd.novadigm.ext' + EOL +
      EOL +
      EOL +
      '------WebKitFormBoundary7MA4YWxkTrZu0gW' + EOL +
      'Content-Disposition: form-data; name="data"' + EOL +
      EOL +
      JsonToSend + EOL +
      '------WebKitFormBoundary7MA4YWxkTrZu0gW--',
      TEncoding.UTF8
    );
    try
      Result := IdHTTP1.Post('https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json', PostData);
    finally
      PostData.Free;
    end;
  finally
    IdHTTP1.Free;
  end;
end;

UPDATE: 根据您提供的新curl 命令,您显示的原始示例与命令不匹配。试试这样的东西:

function TFormJsonWrite.HTTPPost: String;
var
  JsonToSend : String;
  IdHTTP1: TIdHTTP;
  IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
  PostData: TIdMultipartFormDataStream;
begin
  JsonToSend := '[{"file_key": "path", "store_id": <<store_id>>}]';

  IdHTTP1 := TIdHTTP.Create;
  try
    IdHTTP1.ReadTimeout := 50000;

    IdHTTP1.Request.BasicAuthentication := false;
    IdHTTP1.Request.CustomHeaders.Values['Authorization'] := 'Bearer ...';
    IdHTTP1.Request.CustomHeaders.Values['Postman-Token'] := '...';

    IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
    IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvTLSv1_2;
    IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;

    PostData := TIdMultipartFormDataStream.Create;
    try
      PostData.AddFile('path', '<<path_to_file>>').ContentTransfer := 'binary';
      PostData.AddFormField('data', JsonToSend, 'utf-8', 'application/json').ContentTransfer := '8bit';

      Result := IdHTTP1.Post('https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json', PostData);
    finally
      PostData.Free;
    end;
  finally
    IdHTTP1.Free;
  end;
end;

或者:

function TFormJsonWrite.HTTPPost: String;
var
  JsonToSend : String;
  IdHTTP1: TIdHTTP;
  IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
  PostData: TMemoryStream;
  FS: TIdReadFileExclusiveStream;
begin
  JsonToSend := '[{"file_key": "path", "store_id": <<store_id>>}]';

  IdHTTP1 := TIdHTTP.Create;
  try
    IdHTTP1.ReadTimeout := 50000;

    IdHTTP1.Request.ContentType := 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW';
    IdHTTP1.Request.BasicAuthentication := false;
    IdHTTP1.Request.CustomHeaders.Values['Authorization'] := 'Bearer ...';
    IdHTTP1.Request.CustomHeaders.Values['Postman-Token'] := '...';

    IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
    IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvTLSv1_2;
    IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;

    PostData := TMemoryStream.Create;
    try
      WriteStringToStream(PostData, '------WebKitFormBoundary7MA4YWxkTrZu0gW' + EOL);
      WriteStringToStream(PostData, 'Content-Disposition: form-data; name="path"; filename="<<file_name>>"' + EOL);
      WriteStringToStream(PostData, 'Content-Type: ' + GetMIMETypeFromFile('<<file_name>>') + EOL);
      WriteStringToStream(PostData, 'Content-Transfer-Encoding: binary' + EOL);
      WriteStringToStream(PostData, EOL);

      FS := TIdReadFileExclusiveStream.Create('<<path_to_file>>', fmOpenRead or fmShareDenyWrite);
      try
        PostData.CopyFrom(FS, 0);
      finally
        FS.Free;
      end;

      WriteStringToStream(PostData, EOL);
      WriteStringToStream(PostData, '------WebKitFormBoundary7MA4YWxkTrZu0gW' + EOL);
      WriteStringToStream(PostData, 'Content-Disposition: form-data; name="data"' + EOL);
      WriteStringToStream(PostData, EOL);
      WriteStringToStream(PostData, JsonToSend + EOL, IndyTextEncoding_UTF8);
      WriteStringToStream(PostData, '------WebKitFormBoundary7MA4YWxkTrZu0gW--');

      PostData.Position := 0;

      Result := IdHTTP1.Post('https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json', PostData);
    finally
      PostData.Free;
    end;
  finally
    IdHTTP1.Free;
  end;
end;