1

I'd like to upload a file using PUT on TIdHtttp. I found an answer from Remy Lebeau saying to not use PUT, but use POST instead.

But, in my case I can't do it because I'm using a third API, and it specifies that I need to use PUT. If I try to use POST, it returns me a message saying that the Method is not allowed.

Basically, I'm trying to do something like this:

Response := TStringStream.Create;
DS := TIdMultiPartFormDataStream.Create;
DS.AddFile('fileUpload2', 'C:\Users\r.rezino\Desktop\teste.po', 'application/octet-stream');
FHttpComunication.Request.ContentType := 'multipart/form-data';
FHttpComunication.Put(UrlCmd, DS, Response);

But when I do it, I get a 500 - Internal server error.

If I remove the:

FHttpComunication.Request.ContentType := 'multipart/form-data';

I get 400 - Bad Request.

I already tried to do the request directly from browser (Advanced REST client - chrome) and it works. So the API is working. The header of the message when it works is:

PUT /api/2/project/XXXXXXXX/resource/export1po/translation/en/ HTTP/1.1
HOST: www.XXXXXXXXXXXXX.com
authorization: Basic XXXXXXXXXXXXX
content-type: multipart/form-data; boundary=----WebKitFormBoundaryPWT8bUdkQ1ZVLHdL
accept: application/json
accept-encoding: gzip, deflate
accept-language: en-US,en;q=0.8
user-agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
content-length: 1936   

------WebKitFormBoundaryPWT8bUdkQ1ZVLHdL
Content-Disposition: form-data; name="fileUpload2"; filename="teste.po"
Content-Type: application/octet-stream

The code is not here, but yes I configured the authorization information, it's working, I can check this because I can use others call to API.

Update

This is the API that I'm trying to use PUT: http://docs.transifex.com/api/translations/#put

Ways that I tried:

1) This one return 500 - Internal Server Error. I tried it creating the DS object with content type application/octet-stream too. But I got the same error.

Response := TStringStream.Create;
DS := TIdMultiPartFormDataStream.Create;
DS.AddFile('fileUpload2', 'C:\Users\r.rezino\Desktop\teste.po', 'multipart/form-data');
try
    FHttpComunication.Request.ContentType := 'multipart/form-data';
    FHttpComunication.Put(UrlCmd, DS, Response);
    Result := Response.DataString;
finally
    Response.Free;
    DS.Free;
end;

2) The way that was suggested on answers, but It's not working. I got the error 500 too.

PutData := TFileStream.Create('C:\Users\r.rezino\Desktop\teste.po', fmOpenRead or fmShareDenyWrite);
Response := TStringStream.Create;
try
    FHttpComunication.Request.ContentType := 'multipart/form-data'; //I tried with and without it 
    FHttpComunication.Put(UrlCmd, PutData, Response);
    Result := Response.DataString;
finally
    Response.Free;
    PutData.Free;
end;
Rodrigo Farias Rezino
  • 2,687
  • 3
  • 33
  • 60
  • Provide full code with all the headers you set. Last parameter in the `AddFile` procedure should be `application/octet-stream`. Do NOT set `ContentType` manually if you don't use `TIdMultiPartFormDataStream`. Otherwise use `RequestContentType` function as I mentioned. Save your headers from `TIdHTTP.Request.RawHeaders.Text` or use `OnIdHTTPHeadersAvailable` event and compare your headers. If you send exactly the same request, it has to work. – smooty86 Jul 28 '16 at 10:09

2 Answers2

6

That API is broken by design. You should not mix multipart/form-data and PUT. You should send it like this

FPutData:=TFileStream.Create('yourfile.dat', fmOpenRead or fmShareDenyWrite);
FHTTP.Put('http://...', FPutData, FResponseStream);

But if it does not work, the problem is probably that you don't set a correct ContentType, it does not include boundary. Fix it like this

FHTTP.Request.ContentType:=DS.RequestContentType;

Where DS is your TIdMultiPartFormDataStream.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
smooty86
  • 1,112
  • 7
  • 13
  • 2
    `TIdMultipartFormDataStream` is intended to be used with `Post()`, and that version of `Post()` sets the `ContentType` internally, with boundary. If you `Put()` a `TIdMultipartFormDataStream`, then yes, you have to set the `ContentType` manually, and should use the `RequestContentType` for that. – Remy Lebeau Jul 27 '16 at 18:16
  • It's not working! I still get Bad Request. I tried both cases. I odd more information on question. – Rodrigo Farias Rezino Jul 28 '16 at 07:28
1

I found the problem. It was about the boundary that I must to set. So the final result is:

DS := TIdMultiPartFormDataStream.Create;
DS.AddFile('fileUpload2', 'C:\Users\r.rezino\Desktop\teste.po', 'application/octet-stream');
try
    FHttpComunication.Request.ContentType := 'multipart/form-data; boundary=' + DS.Boundary;
    FHttpComunication.Request.AcceptEncoding := '';
    FHttpComunication.Request.Accept := '';
    FHttpComunication.Request.Host := '';
    FHttpComunication.Request.UserAgent := '';

    FHttpComunication.Put(UrlCmd, DS, Response);
finally
    DS.Free;
    Response.Free;
end;
Rodrigo Farias Rezino
  • 2,687
  • 3
  • 33
  • 60