Front-end local file operations and uploads

Source: Internet
Author: User
Tags base64

The front-end can not be as direct as the native app to manipulate local files, otherwise open a Web page will be able to steal the files on the user's computer, so it needs to be triggered by the user, the user can operate in the following three ways trigger:

    1. Select local file via input type= "file"
    2. Drag and drop the file.
    3. Copy and paste inside the edit box

The first is the most commonly used method, and usually also customizes a button and then covers it, because the type= "file" input does not change the style. The following code writes a selection control and puts it inside the form:

<form>    type="file" id="file-input" name="fileContent"></form>

You can then use FormData to get the contents of the entire form:

$("#file-input").on("change", function() {    console.log(`file name is ${this.value}`); let formData = new FormData(this.form); formData.append("fileName", this.value); console.log(formData);});

Print the value and formdata of input as follows:

You can see that the path to the file is a false path, which means that the browser cannot get to the real location of the file. At the same time Formdata print out is an empty objet, but not that its content is empty, but it is transparent to the front-end developers, unable to view, modify, delete content inside, can only append add fields.

Formdata cannot get the contents of the file, and FileReader can read the contents of the entire file. After the user selects the file, Input.files can get the user selected file, the following code:

$("#file-input").on("change", function() {    let fileReader = new FileReader(); fileReader.onload = function() { if (/^image/.test(this.type)) { // 读取结果在fileReader.result里面 $(`<img src="${this.result}">`).appendTo("body"); } } // 打印原始File对象 console.log(this.files[0]); // base64方式读取 fileReader.readAsDataURL(this.files[0]); });

The original file object is printed out like this:

It is a window. An instance of file that contains the modified time, filename, file size, MIME type of the file, and so on. If you need to limit the size of the uploaded file, you can determine whether the value of the Size property is not super, the unit is a byte, and whether the image file can be preceded by an image by type. By judging the suffix of the file name may be inaccurate, and this judgment will be more accurate. The above code uses a regular judgment, if it is a picture, it is assigned to the IMG SRC, and added to the DOM, but in fact, this code is a bit problematic, that is, the web is not all the images can be displayed through the IMG tag, usually jpg/png/gif these three kinds, So you need to judge a piece of format, if you can change the judgment to:

/^image\/[jpeg|png|gif]/.test(this.type)

Then instantiate a filereader, tune its readasdataurl and pass the file object to it, listen to its onload event, and load the read results in its Result property. It is a base64 format and can be directly assigned to an IMG src.

The FileReader can be read in the following format, in addition to being readable as base64:

// 按base64的方式读取,结果是base64,任何文件都可转成base64的形式fileReader.readAsDataURL(this.files[0]);// 以二进制字符串方式读取,结果是二进制内容的utf-8形式,已被废弃了fileReader.readAsBinaryString(this.files[0]);// 以原始二进制方式读取,读取结果可直接转成整数数组fileReader.readAsArrayBuffer(this.files[0]);

The other main is the ability to read as Arraybuffer, which is the result of a primitive binary format. Print the Arraybuffer out like this:

As you can see, it is also transparent to the front-end developers, not able to read the contents directly, but can be obtained by arraybuffer.length the length, but also can be converted to an integer array, you can know the original binary content of the file:

let buffer = this.result;// 依次每字节8位读取,放到一个整数数组let view = new Uint8Array(buffer);console.log(view);

How do you read a file if you are using the second drag-and-drop method ? The following HTML (style slightly):

<div class="img-container">    drop your image here</div>

This will display a box on the page:

Then listen to its drag-and-drop event:

$(".img-container").on("dragover", function (event) {    event.preventDefault();}).on("drop", function(event) { event.preventDefault(); // 数据在event的dataTransfer对象里 let file = event.originalEvent.dataTransfer.files[0]; // 然后就可以使用FileReader进行操作 fileReader.readAsDataURL(file); // 或者是添加到一个FormData let formData = new FormData(); formData.append("fileContent", file);})

Data in the event.dataTransfer.files of the drop event, the file object can be taken after the same operation as the input box, that is, using FileReader read, or create a new empty Formdata, and then append it into the formdata.

The third way to paste , usually in an edit box operation, such as the Div contenteditable set to true:

 <div contenteditable="true">      hello, paste your image here </div>

The pasted data is inside the Event.clipboardData.files:

$("#editor").on("paste", function(event) {    let file = event.originalEvent.clipboardData.files[0];});

But safari paste is not passed through the event, it is directly inside the input box to add a picture, as shown in:

It creates an IMG tag and points the IMG src to a blob of local data. What is a blob, and how do I read the contents of a blob?

A blob is a storage format for a class file that can store content in almost any format, such as JSON:

let data = {hello: "world"};let blob = new Blob([JSON.stringify(data)],  {type : ‘application/json‘});

To get the local BLOB data, we can use Ajax to send a local request:

$("#editor").on("paste", function(event) {    // 需要setTimeout 0等图片出来了再处理    setTimeout(() => { let img = $(this).find("img[src^=‘blob‘]")[0]; console.log(img.src); // 用一个xhr获取blob数据 let xhr = new XMLHttpRequest(); xhr.open("GET", img.src); // 改变mime类型 xhr.responseType = "blob"; xhr.onload = function () { // response就是一个Blob对象 console.log(this.response); }; xhr.send(); }, 0);});

The above code prints the blob out like this:

Can get its size and type, but the specific content is not visible, it has a slice method, can be used to cut large files. As with file, you can use FileReader to read its contents:

function readBlob(blobImg) {    let fileReader = new FileReader();    fileReader.onload = function() { console.log(this.result); } fileReader.onerror = function(err) { console.log(err); } fileReader.readAsDataURL(blobImg);}readBlob(this.response);

In addition to this, window can be used. URL read, this is a new API, often used with service workers, because the SW inside often to parse the URL. The following code:

function readBlob(blobImg) {    let urlCreator = window.URL || window.webkitURL;    // 得到base64结果    let imageUrl = urlCreator.createObjectURL(this.response);    return imageUrl;}readBlob(this.response);

With regard to SRC using a blob link, in addition to the above mentioned IMG, another very common is the video tag, such as Youtobe is the use of the BLOB:

This data is not directly local, but through the continuous request of video data, and then through the BLOB container media added to the video, it is also through the URL of the API created:

let mediaSource = new MediaSource();video.src = URL.createObjectURL(mediaSource);let sourceBuffer = mediaSource.addSourceBuffer(‘video/mp4; codecs="avc1.42E01E, mp4a.40.2"‘);sourceBuffer.appendBuffer(buf);

I have not practiced it, and I will not start the discussion.

Above, we used three ways to get the contents of the file, and finally get:

    1. Formdata format
    2. FileReader read base64 or Arraybuffer binary format

If the direct is a formdata, then directly with the Ajax out of the line, do not have to do any processing:

let form = document.querySelector("form"),    formData = new FormData(form),formData.append("fileName", "photo.png");let xhr = new XMLHttpRequest();// 假设上传文件的接口叫uploadxhr.open("POST", "/upload");xhr.send(formData);

If you are using jquery, set the two property to false:

$.ajax({    "/upload",    type: "POST",    data: formData,    processData: false,  // 不处理数据    contentType: false // 不设置内容类型});

Because jquery automatically escapes the content and automatically sets the request MIME type based on data, it tells jquery to send it directly with Xhr.send.

Observe the data that the console sends the request:

You can see that this is a difference from the use of & connection parameters, its encoding format is Multipart/form-data, is the upload file form form write Enctype:

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

If the xhr.send is a formdata type, it will automatically set the enctype, if you use the default form submission upload file, you have to set this property on the form, because the upload file can only use the post of this encoding. The commonly used post code is application/x-www-form-urlencoded, which, like get, sends the data inside, using & connections between parameters and parameters, such as:

Key1=value1&key2=value2

Special characters are escaped, this data post is placed in the request body, and get is spelled on the URL, if using JQ, JQ will help you spell and do escape.

While uploading a file with this multipart/form-data, the parameters and parameters are separated by an identical string, the above is used:

------webkitformboundary72yvm25ispyz4a3f

This character usually gets longer and more random, because the string is guaranteed not to appear in normal content, so that the special characters of the content do not have to be escaped.

The requested contenttype is set by the browser:

content-type:multipart/form-data, boundary=----webkitformboundary72yvm25ispyz4a3f

The backend service knows how to parse such a piece of data through this. (usually the framework used is handled, and the specific interface does not need to be concerned about how to parse it)

If the reading result is Arraybuffer, but also can be sent directly with xhr.send, but generally we do not directly send the contents of a file, but with a field name is equal to the content of the file. If you read for Arraybuffer and then upload the words in fact is not very large, it is better to add the content of a file object directly with Formdata, because the above three ways can get the file object. If it is a arraybuffer at first, it can be turned into a blob and then append into Formdata.

Use more should be base64, because the front-end often to deal with the picture, read as Base64 after it can be drawn into a canvas, and then can do some processing, such as compression, cropping, rotation and so on. Finally, using canvas to export a base64 format of the picture, how to upload base64 format it?

The first is to spell a form to upload the multipart/form-data format, and then sent out with Xhr.sendasbinary, the following code:

let base64Data = base64Data.replace(/^data:image\/[^;]+;base64,/, "");let boundary = "----------boundaryasoifvlkasldvavoadv";xhr.sendAsBinary([    // name=data    boundary,        ‘Content-Disposition: form-data; name="data"; filename="‘ + fileName + ‘"‘, ‘Content-Type: ‘ + "image/" + fileType, ‘‘, atob(base64Data), boundary, //name=imageType boundary, ‘Content-Disposition: form-data; name="imageType"‘, ‘‘, fileType, boundary + ‘--‘].join(‘\r\n‘));

The above code uses the Window.atob API, which can revert base64 to the string representation of the original content, as shown in:

Btoa is to convert content into Base64 encoding, and Atob is to restore base64. Before you tune Atob, you need to remove the string representing the content format that is not part of the Base64 content, which is the first line of replace on the previous code.

This is similar to using Formdata, but since Sendasbinary has been deprecated, the new code is not recommended for use in this way. What about that?

The base64 can be converted into blobs and then append into a formdata, and the following function (from B64-to-blob) can turn Base64 into a blob:

function b64toBlob(b64Data, contentType, sliceSize) {    contentType = contentType || ‘‘;    sliceSize = sliceSize || 512;    var byteCharacters = atob(b64Data);    var byteArrays = [];    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {      var slice = byteCharacters.slice(offset, offset + sliceSize);      var byteNumbers = new Array(slice.length);      for (var i = 0; i < slice.length; i++) {        byteNumbers[i] = slice.charCodeAt(i);      }      var byteArray = new Uint8Array(byteNumbers);      byteArrays.push(byteArray);    }    var blob = new Blob(byteArrays, {type: contentType}); return blob;}

Then you can append into the formdata:

let blob = b64toBlob(b64Data, "image/png"),    formData = new FormData();formData.append("fileContent", blob);

So you don't have to spell a multipart/form-data format data yourself.

The above processing and uploading the file API can be compatible to ie10+, if you want to be compatible with the old browser should do?

an IFRAMEcan be used, the principle is that the default form form submission refreshes the page, or jumps to the URL specified by target, but if the ifrmae target is pointing to an IFRAME, then the IFRAME is refreshed. The returned results are also displayed in Ifame, and then the contents of this ifrmae are obtained to get the result of the upload interface return.

The following code:

let iframe = document.createElement("iframe");iframe.display = "none";iframe.name = "form-iframe";document.body.appendChild(iframe);// 改变form的targetform.target = "form-iframe";iframe.onload = function() { //获取iframe的内容,即服务返回的数据 let responseText = this.contentDocument.body.textContent || this.contentWindow.document.body.textContent;};form.submit();

Form.submit will be able to contact the delivery of a single submission, when the request is completed (success or failure) will trigger the OnLoad event of the IFRAME, and then in the OnLoad event to obtain the returned data, if the request fails, the contents of the IFrame is empty, you can use this to determine whether the request is successful.

There is no way to get upload progress using an IFRAME, using XHR to get the current upload progress, which was introduced in XMLHttpRequest 2.0:

function (event) {    if (event.lengthComputable) {        // 当前上传进度的百分比        duringCallback ((event.loaded / event.total)*100);    }};

This makes it possible to make a real loading progress bar.

In this paper, we discuss the reading methods of 3 kinds of interactive methods, The file object can be obtained through the input control in Input.files, by dragging the event.dataTransfer.files inside the drop event, and by pasting the paste event inside the Event.clipboardData.files, Safa Ri This geek is inserting a src pointer to the local IMG tag in the editor, which can load the local BLOB data by sending a request, and then read it through FileReader, or append it directly into Formdata. The resulting file object can be added directly to the Formdata, if you need to read the Base64 format to do processing, then you can convert the processed base64 into BLOB data and append into the formdata inside. For the old browser, you can use an IFRAME to resolve the issue of the form submission Refresh page or skip page.

In short, the front-end processing and uploading of local files should be almost the same content, but there should be a lot of details not mentioned, readers can be in the direction of this article in their own practice. If there are other ways to upload, please let us know


Renren Fed
Links: https://juejin.im/post/5a193b4bf265da43052e528a
Source: Denver Nuggets
Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.

Front-end local file operations and uploads

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.