1

I am trying to create an FMX Android application that needs to upload a file to given host.

The host owner provided me with a following curl statement to upload a file:

curl -T http://<ip-address>:<port> file.txt

And when called from Windows Command Prompt it produces the following output (IP and port redacted):

*   Trying <ip-address>...
* TCP_NODELAY set
* Connected to <ip-address> (<ip-address>) port <port> (#0)
> PUT /file.txt HTTP/1.1
> Host: <ip-address>:<port>
> User-Agent: curl/7.55.1
> Accept: */*
> Content-Length: 682
> Expect: 100-continue
>
* Done waiting for 100-continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< content-length: 0
< date: Tue, 16 Jun 2020 19:24:18 GMT
< connection: close
<
* Closing connection 0

and file is visible in the expected directory on the host machine.

Now I'm trying to have the same behavior using Indy, but either there is some black-box magic occurring on the server-side, or there is an issue with my code - with some solutions the request seems to finalize, but there is no file in the specified directory (and status code 200 is returned).

I have also tried using Fiddler to reproduce the curl call, and in the response of a composed query also received 200 status code, but still - no file was properly uploaded.

When inspecting Wireshark packets for these solutions they all seemed similar to the curl call - however, I am not an expert in using this tool, so maybe there are issues not visible to my newbie eye.

I tried using following solutions:
- How do I upload a file using http post? Delphi 2009 - but using PUT instead of POST
- Single file upload example
- Post a file through https using indy / delphi components

And in my case they do not work - so my guess is that the server is somehow not accepting these.

I know it's very hard to say what I'm doing wrong without me actually putting some code, but right now it's a total mess - but maybe it's not hard to somehow show me how to convert this curl call to work with TIdHttp?

If not, I will try to modify my question soon with more information.

EDIT: As requested, I'm adding some sample code:

var
  FHTTP: TIdHTTP;
  file2send: string;
  sr: TStringStream;
  FPutData : TFileStream;
begin
  sr := TStringStream.Create;
  if OpenDialog1.Execute then
    file2send := OpenDialog1.FileName;

  FHTTP := TIdHTTP.Create(self);
  FPutData := TFileStream.Create(file2send, fmOpenRead or
    fmShareDenyWrite);
  FHTTP.Put(PutURL.text, FPutData, sr);

end;
Asunez
  • 2,327
  • 1
  • 23
  • 46
  • Please show the actual code you are having trouble with. The curl command you have shown is very easy to replicate using `TIdHTTP.Put()` and a `TFileStream`. But, if you can't even produce a valid result using Fiddler instead of Indy, then chances are there is a problem on the server side. – Remy Lebeau Jun 16 '20 at 18:44
  • Hi @RemyLebeau, thanks for your response. As per request, I added some sample code. Could you verify if I'm using Indy and FileStream properly? – Asunez Jun 16 '20 at 20:17

1 Answers1

3

You don't need the TStringStream since you don't use it. You can pass TStream(nil) in that parameter.

Also, you are calling FHTTP.Put() unconditionally, even if OpenDialog.Execute() returns false.

Also, make sure the URL you pass to FHTTP.Put() is 'http://<ip-address>:<port>/file.txt' not 'http://<ip-address>:<port>'.

Also, consider setting FHTTP.Request.UserAgent, as some web servers reject Indy's default UserAgent.

Also, consider Free'ing your objects, especially if you ever plan to upgrade to 10.4 Sydney or later, since ARC for object lifetime management was removed in 10.4.

Try something more like this:

var
  FHTTP: TIdHTTP;
  file2send: string;
  FPutData : TFileStream;
begin
  if not OpenDialog1.Execute then Exit;

  file2send := OpenDialog1.FileName;

  FHTTP := TIdHTTP.Create(nil);
  try
    FHTTP.Request.UserAgent := 'curl/7.55.1';
    FPutData := TFileStream.Create(file2send, fmOpenRead or fmShareDenyWrite);
    try
      FHTTP.Put(PutURL.Text, FPutData, TStream(nil));
      { or, maybe something like:
      URL := PutURL.Text + '/' + ExtractFileName(file2send);
      FHTTP.Put(URL, FPutData, TStream(nil));
      }
    finally
      FPutData.Free;
    end;
  finally
    FHTTP.Free;
  end;
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    "Also, make sure the URL you pass to FHTTP.Put() is 'http://:/file.txt' not 'http://:'." <- this! I'm not sure how I missed this. Works for Windows application, so now I only need to check if Android works properly, but I think the answer to the question is this. For your other remarks (conditional running of Put(), setting user agent and freeing my objects - this was minimal example, but none the less, they are very helpful. Thank you for your time and support :) – Asunez Jun 17 '20 at 06:18