Jump to content

Uploading a file that has been selected using INPUT box type=itFile


Recommended Posts

I am using DMVC and SMS and the user is going to be uploading a file to the server.  I can create a simple HTML form which will complete the upload to the DMVC server, but I can't quite figure out how to approach it using SMS.

I am hoping to get pointed in the right direction or have some suggested options for uploading the file. This is just something that is going to be simple and I'll save the cool features for later. Ideally I was hoping to upload the file in the background and then provide the user feedback once it was done. Nothing fancy like a progress indicator is needed as these files are small,

Are there any samples or places I can look.  I am using version 3.0.2.20.

Thanks.

tim

Link to post
Share on other sites
  • Moderators

I had a look at the DelphiMVCFramework files-upload demo. The upload page, see below, lets the user select a file and submits a form with the selected file to the server.

<html>
<body>
<h2>DMVCFramework - FileUpload DEMO</h2>
<form action="/file" method="post" enctype="multipart/form-data">
<input type="file" name="fupload">
<input type="submit">
</form>
</body>
</html>

To emulate this in Smart, the <input type="file ... line will be taken care of by an edit box with type =itFile. You will have to set the name attribute as well : 

procedure TForm1.InitializeForm;
begin
  inherited;
  // this is a good place to initialize components
  var EditBox1 : TW3EditBox := TW3EditBox.Create(self);
  EditBox1.SetBounds(50,50,400,30);
  EditBox1.InputType := itFile;
  EditBox1.handle.setAttribute('name','fupload');

 
The tricky part is to emulate the <form> element.
While it is doable to make a new component based on the <form> element, it is probably easier to use a FormData object.

FormData objects can capture the data entered in forms and these objects can then just be sent server-side using normal xmlhttprequests. 
See here.

You can link this ajax call to a button, or alternatively to the onchange event of the editbox :

  EditBox1.OnChanged := procedure(sender:TObject)
  begin
    var formData : variant := new JObject;
    asm @formData = new FormData(); end;
    formData.append("userfile", EditBox1.handle.files[0]);

    var FHttp : TW3HttpRequest := TW3HttpRequest.Create;
    FHttp.OnDataReady := nil;  //or a callback procedure
    FHttp.open("POST","/file");
    FHttp.RequestHeaders.Add("Content-type","multipart/form-data"); 
    FHttp.send(formData);
  end;
end;

I have not used the DelphiMVCFramework before and don't have it installed, so the above may not work. But this is how I would attack this.

 

Link to post
Share on other sites

Thank you for the help. It has gotten me closer, but the SMS approach does not seem to be capturing the data.

Also, remember, I am using DMVC to receive the POST, but for the SMS side, I could just as easily be using a server running PHP, NODE.JS or some other mechanism to receive and process the file. My question is more general about how SMS can emulate a form POST to upload the file.

Now to the example. I can see the SMS example posting to the correct page, but not the actual data of the file upload component. Using the inspector in Chrome or FireFox and watching the Network activity, for the SMS example in the data section I only see the following which seems to just the the object name and not the data.

[object FormData]

Now when I compare and use a standard HTML5 form almost exactly like the sample you posted above, the network watcher in the Inspector of the browser shows the following in the Params section for the post which is a sample text file I am posting. It is the same file used via the SMS form and the HTML form.

-----------------------------244962545219104
Content-Disposition: form-data; name="fupload"; filename="Hello.txt"
Content-Type: text/plain

I am a plain text document.
-----------------------------244962545219104--

Any thoughts on what might be happening here. I will dig a little deeper into the Javascript processing of FormData, but I'm hoping this feed back can help. I find SMS to be a great tool, but I feel like a beginner still learning this tool.

Thanks.

Tim

Link to post
Share on other sites
  • Moderators

Post rewritten.

If using the html <form> element, as below, works for a DMVC server, then it will work for other servers like php, node as well.

<form action="/file" method="post" enctype="multipart/form-data">
<input type="file" name="fupload">
<input type="submit">
</form>

So the best bet would be to follow this same mechanism in Smart. The questions are then 

  • A:  since there is no component in Smart which encapsulates the <form> element, how can we get one, or
  • B:  would it be possible to circumvent this <form> element completely

Ad B (the difficult one first)

This involves looking at what exactly happens when a <form> is used for file uploads, and reproduce that process in object pascal. Personally I am not in favour of this approach as it duplicates functionality which is freely available in the browser. 

However, it can be done. The <form> submit process for uploading files goes something like

  • select a file to upload (type = itFile)
  • read this file using the File api
  • chop the content up in chunks
  • surround each chunk with a unique border (something like "-----------------------------244962545219104")
  • send these chunks using the xmlhttp post protocol
  • using some specific headers

There is an example here using this approach (with a simple php server as backend). A modified working version is available here, which gives as output (FF Params tab) something similar to

-----------------------------168b2ce8400
Content-Disposition: form-data; name="myfile"; filename="bar.txt"
Content-Type: text/plain

test file
-----------------------------168b2ce8400--

If this looks promising, the next step would be to translate the js framework into object pascal.

 

Ad A

It should be feasible to create a new component which encapsulates a <form> element.

Stub like

  TW3FormElement = Class(TW3CustomControl)
  protected
    function  MakeElementTagId: string; override;
    Procedure ObjectReady;Override;
    procedure InitializeObject;override;
  End;

implementation

function TW3FormElement.MakeElementTagId: String;
Begin
  result:='FORM';
end;

 

Note

There is another thread which might be of use as well : 

 

 

Link to post
Share on other sites

Ok, this first follow up is what I did to get it working based on some of the links for handling forms in javascript. Fortunately because I could mix and match SMS Pascal and Javascript, I get the best of both worlds.

On my Smart Form, I have a TW3Button and a TW3EditBox with a type of ltFile. Then with the following javascript, I can get the form posted correctly to a backend which is able to capture the file and other variables of the form. Note that the other elements are created dynamically based on an example I was using. The ShowMessage and Alert items were just feedback mechanisms for me to see this part working. Both my DMVC server and a PHP script were able to process this.

I am sure that there is a more friendly SMS way of writing this, but for now it got my form working and I was able to continue to plow forward with this upload piece being reasonably well contained. My key take way was getting the file upload field and the code below did the trick. This may not be the best way, but at least it was a starting point.

myFile = document.getElementById('id', 'fupload1');

The InitializeObject was important to label the TW3EditBox element correctly. Below that is the code that I stuck in the OnClick event for the button. I saw many other ways to deal with this, but I just wanted to keep it straight forward for the UI.
 

procedure TForm_ImportFile.InitializeObject;
begin
  inherited;
  {$I 'Form_ImportFile:impl'}
  W3ButtonBack.InnerHTML := '<i class="fa fa-arrow-left fa-2x"></i>';

    // this is a good place to initialize components
  W3EditBox1.handle.setAttribute('name','fupload');
  W3EditBox1.handle.setAttribute('id', 'fupload1');  // Initialize EditBox id to find it with getElementById below
  W3EditBox1.InputType := itFile;
end;

// BUTTON CLICK EVENT CODE
  ShowMessage('Starting');
  asm
    var formData = new FormData();

    alert('Trying to get file box');
    myFile = document.getElementById("fupload1").files[0];
    alert('Got file: ' + myFile.name);
    formData.append("username", "Groucho");
    formData.append("accountnum", 123456); // number 123456 is immediately converted to a string "123456"

    // HTML file input, chosen by user
    //formData.append("fupload", fileInputElement.files[0]);

    //var myFile = new Blob([content], {type: "text/plain"});
    formData.append("userfile", myFile, myFile.name);

    // JavaScript file-like object
    var content = '<a id="a"><b id="b">hey!</b></a>'; // the body of the new file...
    var blob = new Blob([content], { type: "text/xml"});

    formData.append("webmasterfile", blob);

    var request = new XMLHttpRequest();
    request.open("POST", "http://127.0.0.1:2001/file");
    request.send(formData);
  end;
  ShowMessage('Done');

 

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...