Three methods of uploading files without refreshing the page discuss iframe/FormData/FileReader

There are two ways to send a request, one is to use ajax, and the other is to use form submission. If the default form submission is not processed, the page will be redirected. To illustrate with a simple demo:

The html is as follows, the requested path action is "upload", and the others do not do any processing:

<form method="POST" action="upload" enctype="multipart/form-data">
        名字 <input type="text" name="user"></input>
        头像 <input type="file" name="file"></input>
        <input type="submit" id="_submit" value="提交"></input>
   </form>

The server (node) response directly returns: "Recieved form data", the demonstration is as follows:

You can see that by default, the form requests upload and redirects to upload at the same time. But in many cases, it is hoped that the form request will not redirect or refresh the page like ajax. Like the above scenario, when the upload is complete, the avatar selected by the user is displayed on the current page.

The first solution is to use the FormData of html5, encapsulate the data in the form into the FormData object, and then send it out by POST. As shown in the following code, a response is made to the click event of the submit button. The 6th line of the code obtains the DOM object of the form, and then the 8th line constructs an instance of FormData, and the 18th line sends the form data.

1     document.getElementById("_submit").onclick = function(event){
 2 //Cancel the default form submission method
 3           if(event.preventDefault) event.preventDefault();
 4 else event.returnValue = false; //Cancellation method for IE
 5   
 6           var formDOM = document.getElementsByTagName("form")[0];
 7 //Take the DOM object of the form as the constructor of FormData
 8 var formData = new FormData(formDOM);
 9           var req = new XMLHttpRequest();
10           req.open("POST", "upload");
11 //Request completed
12           req.onload = function(){
13              if(this.status === 200){
14 //Processing of successful request
15              }
16           }
17 //Send the form data
18           req.send(formData);
19 //Avoid memory leaks
20       req = null;
21 }

After the upload is successful, the service will return the access address of the image, and add 14 lines to handle the successful request: display the uploaded image at the top of the submit button:

1                 var img = document.createElement("img");
2                 img.src = JSON.parse(this.responseText).path;
3                 formDOM.insertBefore(img, document.getElementById("_submit"));

Example:

If you use jQuery, you can use formData as the data parameter of ajax, and set contentType: false and processData: false at the same time to tell jQuery not to process the request header and sent data. 

It seems that this submission method is the same as ajax, but it is not exactly the same. There are three data formats for form submission  . If you want to upload a file, it must be multipart/form-data, so the http header in the form submission request above The Content-Type in the information is multipart/form-data, and the normal ajax submission is application/json. The complete Content-Type submitted by the form is as follows:

"content-type":"multipart/form-data; boundary=------WebKitFormBoundaryYOE7pWLqdFYSeBFj"

In addition to multipart/form-data, boundary is also specified, which is used to distinguish different fields. Since the FormData object is opaque, calling JSON.stringify will return an empty object {}, and FormData only provides the append method, so the actual uploaded content of FormData cannot be obtained, but it can be viewed through the data received by analysis tools or services . In the above, if a text file is uploaded, the original format of the POST data received by the service is as follows:

------WebKitFormBoundaryYOE7pWLqdFYSeBFj
Content-Disposition: form-data; name="user"

abc
------WebKitFormBoundaryYOE7pWLqdFYSeBFj
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain

This is the content of a text file.

------WebKitFormBoundaryYOE7pWLqdFYSeBFj--

From the data received by the above service, we can see the format submitted by FormData. Each field is separated by boundary and ends with --. For ajax requests, the data format sent out is customized, generally connected with & in the middle of key=value:

var req = new XMLHttpRequest();
        var sendData = "user=abc&file=this is the content of a text file";
        req.open("POST", "upload");
        //The data sent needs to be escaped, see the three formats mentioned above
        req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        req.send(sendData);

The service will receive the exact same content as the string sent by send, and then parse the parameters, so the format of the parameters must be unified:

user=abc&file=This is the content of a text file

It can be seen from this that POST is not inherently more secure than GET. POST just does not send the data in the URL.

Considering that FormData is not supported until IE10, if you want to support lower versions of IE, you can use iframe.

At the beginning of the article, it was said that the default form submission will redirect the page, and the redirection rule is specified in the  target,  which can be specified as "_blank" like the a tag, which opens in a new window; it can also be specified as an iframe, Open in that iframe. So you can make a hidden iframe and point the target of the form to this iframe. When the form request is completed, the returned data will be displayed by this iframe, as shown above on the new page: "Recieved form data". After the request is completed, the iframe is loaded and the load event is triggered. In the handler function of the load event, the content of the iframe is obtained, and the data returned by the service is obtained! After getting it, delete the iframe.

In the submit button's response function, first create an iframe, set the iframe to be invisible, and then add it to the document:

var iframe = document.createElement("iframe");
        iframe.width = 0;
        iframe.height = 0;
        iframe.border = 0;
        iframe.name = "form-iframe";
        iframe.id = "form-iframe";
        iframe.setAttribute("style", "width:0;height:0;border:none");
        // put it in document
        this.form.appendChild(iframe);

Change the target of the form to the name value of the iframe:

this.form.target = "form-iframe";

Then respond to the load event of the iframe:

iframe.onload = function(){
            var img = document.createElement("img");
            //Get the content of the iframe, that is, the data returned by the service
            var responseData = this.contentDocument.body.textContent || this.contentWindow.document.body.textContent;
            img.src = JSON.parse(responseData).path;
            f.insertBefore(img, document.getElementById("_submit"));
            // delete iframe
            setTimeout(function(){
                var _frame = document.getElementById("form-iframe");
                _frame.parentNode.removeChild(_frame);
            }, 100);
            //If the prompt submit function does not exist, please pay attention to whether there is a control with id/value as submit in the form
            this.form.submit();
        }

The second method is basically enough here, but if you look at the way of uploading files by 163 mailbox or QQ mailbox, you will find that it is different from the above two methods. Using httpfox to grab the requested data, you will find that the format of the uploaded content is not separated by boundary as mentioned above, but directly POSTs the content of the file, and the file name, file size and other related information are placed in the file. head. Such as 163 mailbox:

POST Data:
    this is a text

Headers:
    Mail-Upload-name: content.txt
    Mail-Upload-size: 15

It can be speculated that they should directly read the content of the input file, and then POST directly. To achieve such a function, you can use FileReader to read the content of the input file, and then send it out in binary format:

1         var req = new XMLHttpRequest();
 2         req.open("POST", "upload");
 3 //Set the same Content-Type as the mailbox
 4         req.setRequestHeader("Content-Type", "application/octet-stream");
 5         var fr = new FileReader();
 6         fr.onload = function(){
 7             req.sendAsBinary(this.result);
 8         }
 9         req.onload = function(){
10 // same, omit
11         }
12 //Read the content of the input file and put it in the result field of fileReader
13         fr.readAsBinaryString(this.form["file"].files[0]);

The 13th line of the code executes the read file, and after the reading is completed, the load response function on the 6th line is triggered, and the 7th line is sent out in the form of binary text. Since the support of sendAsBinary is not very good, you can  implement one yourself  :

1   if(typeof XMLHttpRequest.prototype.sendAsBinary === 'undefined'){
 2       XMLHttpRequest.prototype.sendAsBinary = function(text){
 3       var data = new ArrayBuffer(text.length);
 4       var ui8a = new Uint8Array(data, 0);
 5       for (var i = 0; i < text.length; i++){
 6           ui8a[i] = (text.charCodeAt(i) & 0xff);
 7       }
 8       this.send(ui8a);
 9     }
10   }

The key to the code is line 6, which converts the string into an 8-bit unsigned integer and restores the contents of the binary file. After executing fr.readAsBinaryString, the content of the binary file will be stored in the result in the form of a string in utf-8 encoding. The above line 6 converts each unicode encoding into an integer (&0xff or parseInt) and stores it into an 8-bit unsigned integer array, and line 8 sends the array out. If you send directly instead of sendAsBinary, the data received by the service will not be restored to the original file normally.

The above implementation needs to consider the problem that the file is too large and needs to be uploaded in sections.

Regarding the supportability of FileReader   , IE10 and above are supported, and IE9 has another set of File API.

The article discusses 3 ways to upload files without refresh, namely using iframe, FormData and FileReader. The best support is iframe, but from the effect of experience, FormData and FileReader are better, because they do not need to generate a useless The DOM is then deleted, of which FormData is the easiest, and FileReader is more flexible.

refer to:

1. Ajax Style File Uploading using Hidden iFrame

2.  Using files in web applications

3. AJAX File Uploads with the iFrame Method

4.  Using the FormData object

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326219041&siteId=291194637