Я пытаюсь загрузить файл 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
на неправильное значение.
Вы публикуете данные JSON как есть, вообще не помещая их в MIME.
НЕ устанавливайте свойство TIdHTTP.Request.AcceptEncoding
вручную. Вы не настраиваете TIdHTTP
для включения поддержки сжатия, но вы даете серверу разрешение отправлять сжатые ответы, которые TIdHTTP
не смогут распаковать для вас.
Способ правильный отправить запрос multipart/form-data
с использованием TIdHTTP
заключается в использовании перегруженного метода 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
и/или Content-Transfer-Encoding
, которые генерируются 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;
ОБНОВИТЬ: на основе новых команд 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;
Я добавил завиток, и похоже, он работает, за исключением ошибки с файлом json, который составляет 952 КБ.
Я добавил новый завиток.
Я только что протестировал новый curl с Git Bash, и он работает, но я все еще получаю 403, используя Delphi Indy. Любые предложения, спасибо.
EOL
определяется в единицах IdGlobal
Инди. Да, это \r\n
. Ошибка HTTP 403 означает, что ваша аутентификация неверна, либо из-за того, что токен OAuth устарел или иным образом недействителен, либо, возможно, вы пропустили шаг в процессе аутентификации, такой как запрос токена OAuth или получение файлов cookie, которые необходимо отправить обратно, и т. д. Что касается команд curl, я обновил свой ответ, чтобы отразить их. Если вы все еще получаете ошибки, вам нужно зафиксировать фактические HTTP-запросы, которые отправляет curl, и сравнить их с запросами, которые отправляет TIdHTTP
.
Спасибо, Реми, TIdMultipartFormDataStream работает безупречно. Я ценю ваше время, чтобы помочь мне, второй раз, когда вы спасли меня за последний месяц.
Спасибо, Реми. Я пробовал оба. Первый выдает мне исключение EidHTTPProtocolException с сообщением «HTTP/1.1 403 Forbidden. Второй я предположил, что EOL был строкой с EOL := '\r\n'. Я вижу, что в TStringStreamCreate есть имя файла = "myjson.json", но я не понимаю, как он получает содержимое файла в TStringStream. Я получаю внутренний сервер 500