I'm trying to upload a JSON file to the SKIP (getskip.com) web server and I am getting socket error 10054. They are not very good at giving samples of how, since they think everyone is using cUrl, but we are still using Delphi XE6 with Indy.
Here is what they provided.
For Java using 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 using 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 using 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;
}
Here is what I tried in Indy. I pasted the JSON file in to a Memo on the Form:
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;
curl
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"}
They updating all their endpoints this is the latest I got.
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>}]'
Your Delphi code is all wrong.
You are setting the wrong Content-Disposition
header to the wrong value.
You are posting the JSON data as-is without wrapping it inside of MIME at all.
DO NOT set the TIdHTTP.Request.AcceptEncoding
property manually. You are not setting up the TIdHTTP
to enable compression support, but you are giving the server permission to send compressed responses, which TIdHTTP
will not be able to decompress for you.
The correct way to send a multipart/form-data
request using TIdHTTP
is to use the overloaded Post()
method that takes a TIdMultipartFormDataStream
as input, like this:
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;
If, by chance, the server has issues with how TIdMultipartFormDataStream
formats the MIME data (ie, if the server rejects the MIME Content-Type
and/or Content-Transfer-Encoding
headers that are generated by TIdMultipartFormDataStream
for each MIME field, as it does not yet conform to RFC 7578, which is used by HTML5), you can format the MIME data manually to match SKIP's examples exactly, like this:
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: based on the new curl commands you have provided, the original examples you showed do not match the commands. Try something more like this instead:
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;
Or:
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;