File upload and download Refers to the process of uploading local files such as pictures, videos, and audio files to the server for other users to browse or download.

Form upload

The main characteristics of this approach are:

  • The most primitive method, now the browser basically support;
  • Method must be the size of the GET request because it is an upload filePOST;
  • Input must be setnameAttribute to identify form data after submission to the server. Only form elements with the name attribute can pass their values when submitting the form.
  • Accept: Specifies the file type that can be submitted through file upload. This attribute is not set and the file upload type should be verified on the server.
  • Multiple: Whether you can select multiple files.
  • The encType property specifies how the form data should be encoded before being sent to the server, i.e. in the request headerContent-TypeThere are three main types of values,application/x-www-form-urlencoded,multipart/form-data,text/plain. If encType in the form does not specify a value, the form defaults to the first submission, but all that is submitted is a string of file names, with ampersands in multiple name=value key-value pairs. So you need to usemultipart/form-datamultipart/form-dataIs used to upload binary files in HTML documents;
  • Styles need to be optimized, usually setinput.style.opacity = 0To hide the input and design the label to look like a button.
<form method="post" action="xxx" enctype="multipart/form-data">
  <div>
    <label for="file">Choose file to upload</label>
    <input type="file" id="file" name="file" multiple>
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>

<script>
  const input = document.querySelector('input');
  input.style.opacity = 0;
  
  input.addEventListener('change', function(e) {
      console.log(e.target.files[0]);
      let formData = new FormData();
      formData.append("fileName", e.target.files[0]);
      console.log(formData);
  })
})
  
</script>
Copy the code

The printed results are as follows:

You can see some information about the file in the print result, but the FormData is printed with an empty {} because it cannot be viewed. You need to use the built-in method, which we’ll explain later.

However, the Form will jump to the page after uploading the file because of the target attribute of the Form. If you want to make users feel like uploading files asynchronously, you can use Framename to specify iframe. If you set the target property of the form to an invisible iframe, the returned data will be accepted by the iframe. Therefore, only the iframe will be refreshed, and the returned result can also be obtained by parsing the text within the iframe.

Ajax upload

  • The server provides the interface, sets the corresponding request header, front-end submissionFormDataForm of file data;
  • Gets the DOM object of the download input box, listeningchangeEvent to get the contents of the file;
  • Use the FormDataappendMethod to add values,e.target.filesIt is aFileListInstance, an array-like object used to store the files selected by the user;
  • If xhr.send is of FormData type, encType is automatically set.
  • The XMLHttpRequest object provides oneprogressEvent, which returns the uploaded size and total size of the file. From these two values, upload progress can be calculated.
<input type="file"> <script> const input = document.querySelector('input'); input.addEventListener('change', function(e) { let fileList = e.target.files; let formdata = new FormData(); // single file formdata.append('file', fileList[0]); / multiple file uploads need to be traversed to add to the fromData object for(let I = 0; i < fileList.length; i++){ formdata.append('f1', fileList[i]); } let xhr = new XMLHttpRequest(); xhr.open("POST","xxxxx"); xhr.onreadystatechange = function () { if(xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText); Xhr.upload. onprogress = function(e) {if (e.lengthcomputable) {let percent = math.floor (event.loaded / event.total * 100); } } xhr.send(formdata); }) </script>Copy the code

Drag and drop to upload

  • Div. Drop-box defines a drag-and-drop area for files and listens for drag-and-drop events.
  • A complete drag-and-upload behavior covers four events:Dragover, Dragenter, drop, dragLeave, just focus on the element being draggeddragoverdrop;
  • Cancels the default behavior of the DROP evente.preventDefault()Otherwise, the browser’s default behavior is to open the file directly;
  • To bind events to the drag region, mouse over the drag regiondragover, mouse away from the drag areadragleaveTo release the file on the drag areadrop;
  • Drag-and-drop data information is stored when dragging and droppingDataTransferIn the object;
  • The remaining steps andFormThe form upload is consistent.
<script> const dragBox = document.querySelector('drag'); dragBox.addEventListener("dragover",function (e) { e.preventDefault(); }) dragBox.addEventListener("drop", function (e) { e.preventDefault(); const fileList = e.dataTransfer.files; }) </script>Copy the code

Large file upload

The main problem with large file uploads is that a large amount of data has to be uploaded in the same request, resulting in a lengthy process and the need to start all over again after a failure. If we split the request into multiple requests, the time for each request is shortened, and if a request fails, we only need to resend the request. Form upload and iframe upload without refreshing page are actually file upload via form tag. In this way, the entire request is completely handed over to the browser. When uploading a large file, the request may time out and cannot be divided into multiple requests. FromData actually encapsulates a set of request parameters in XHR to simulate form requests, and you can’t avoid large file upload timeouts. So by using coded upload, we can control the content of upload more flexibly.

Based on the above problems, the following requirements need to be realized:

  • Support split upload requests (i.e. slices)
  • Support breakpoint continuation
  • Display upload progress and pause upload

File section

The FIle object is a subclass of the Blob object, which contains an important method, slice, through which we can slice binary files.

  • Define the size variable of each shard file as chunkSize.
  • Chunks are obtained by FileSize and chunkSize.
  • Use the for loop and file.slice() method to fragment the file, numbered 0-n;
  • Compare with the uploaded slice list to get all unuploaded slices;
  • Push to the requestList.

Here is an example

function slice(file, piece = 1024 * 1024 * 5) { let totalSize = file.size; // let start = 0; // let end = start + piece; // End byte let chunks = [] while (start < totalSize) {// End byte let chunks = [] while (start < totalSize) { So include the slice method let blob = file.slice(start, end); chunks.push(blob) start = end; end = start + piece; } return chunks }Copy the code

Restore section

Breakpoint continuingly

Upload to suspend

The relevant knowledge

In Web development, when we work with files (create, upload, download), we often encounter binary data. All of this can be handled in JavaScript, and binary operations are more efficient. The main ones are FormData, ArrayBuffer, Blob, and File

ArrayBuffer

  • ArrayBufferIs a basic binary object, the basis of everything, a reference to a contiguous memory space of fixed length. You can think of it as a (large) block of memory
  • ArrayBufferThe point of existence is to write in memory ahead of time as a data source, that is, to pin in some area ahead of time, and the length is fixed for ten thousand years, so when we’re dealing with thisArrayBufferBinary data in, for example, 8 -, 16 -, and 32 – bit conversions will not change, and the three conversions share data
  • We can get throughnew ArrayBuffer(length)To get a contiguous piece of memory that we can’t access directlyArrayBufferBytes inside, which can be passed to as neededTypedArrayView orDataViewObject to interpret the raw buffer. The view really just gives you some kind of read-write interface that you can manipulateArrayBufferThe view object itself doesn’t store anything. It is a pair of “glasses” through which to interpret storage inArrayBufferThe byte

There is a constructor:

let buffer = new ArrayBuffer(16); // Create a buffer of 16 bytes and allocate a contiguous memory space of 16 bytesCopy the code

To manipulate an ArrayBuffer, we need to use a “view” object, the generic term being TypedArray:

  • Uint8ArrayTreat each byte in the ArrayBuffer as a single number between 0 and 255 (each byte is 8 bits, so it can only hold so many). This is called”An 8-bit unsigned integer
  • Uint16Array— Treat every 2 bytes as an integer between 0 and 65535. This is called”16 - bit unsigned integer
  • Uint32Array— Treat every 4 bytes as an integer between 0 and 4294967295. This is called”32 - bit unsigned integer
  • Float64ArrayTreat every 8 bytes as a floating point number between 5.0×10-324 and 1.8×10308

Like:

FormData

FormData is a native HTML5 object that represents an object for FormData. The constructor is:

let formData = new FormData([form]);
Copy the code

We can modify fields in FormData using the following methods:

  • formData.append(name, value)— Add has a givennamevalueForm field of
  • formData.append(name, blob, fileName)Add a field as if it were<input type="file">, the third parameterfileNameSet the file name (not the form field name) because it is the name of the file in the user’s file system
  • formData.delete(name)— Removes a givennameThe field of
  • formData.get(name)— Gets with a givennameThe field values of the
  • formData.has(name)If there exists with a givennameIs returnedtrueOtherwise returnfalse

FormData allows you to wrap a Form or a series of fields as an object, simulate a series of Form controls through key-value pairs, and send them Ajax via standard XHR.

This is different from the & concatenation argument, which is encoded as multipart/form-data, and has the same form as sending data using the form submit() method with the form encType attribute set to multipart/form-data. The request header is content-Type: multipart/form-data. A boundary = – WebKitFormBoundaryVXQzCH5gOtjud1Xu WebKitFormBoundary72yvM25iSPYZ4a3F behind for 16 random Base64 encoded string

------WebKitFormBoundary72yvM25iSPYZ4a3F
Copy the code

Blob

  • BlobOne of the general terms used in computer science:BLOB (binary large object)Represents a binary large objectMySql/OracleThere’s one in the databaseBlobType for storing binary data
  • Binary raw data but file-like objects that can be manipulated as file objectsBlobObject, soBlobIs binary data with type, so it can be convenientBlobUsed to upload or download files in the browser
  • BlobConsists of an optional stringtype(usually MIME type) andblobPartsComposition,blobPartsCan beBlob/BufferSource/StringType, as follows

The constructor is:

new Blob(blobParts, options);
Copy the code

It has two properties:

  • size(read-only) : Represents the size, in bytes, of the data contained in the Blob object.
  • type(read only) : A string indicating the MIME type of the data contained in the Blob object. If the type is unknown, the value is an empty string

Blobs can easily be used as urls for A and IMG or other tags to display their contents

Blob as a URL

Below is a simulated user click to download automatically

let link = document.createElement('a');
link.download = 'hello.txt';

let blob = new Blob(['Hello, world!'], {type: 'text/plain'});
console.log(blob)

link.href = URL.createObjectURL(blob);

link.click();

URL.revokeObjectURL(link.href);
Copy the code

The print result is as follows:

Url.createobjecturl takes a Blob and creates a unique URL for it of the form Blob :

/

. Note that even with the same binary data, every time url.createObjecturl is called, a different Blob URL is obtained. Url.revokeobjecturl (URL) removes references from the internal map and frees memory

let link = document.createElement('a'); link.download = 'hello.txt'; let blob = new Blob(['Hello, world!'], {type: 'text/plain'}); let reader = new FileReader(); reader.readAsDataURL(blob); // Convert Blob to base64 and call onload reader.onload = function() {link.href = reader.result; // data url link.click(); }Copy the code
Blob converts to Base64
  • willBlobconvertbase64The encoded string can also create a URL
  • This encoding represents binary data as a string of 0 to 64 ASCII characters, which is very secure and “readable”. What’s more — we can do it in the”data-url“To use this encoding.
  • data-urlIs in the form ofdata:[<mediatype>][;base64],<data>. We can use this url anywhere, just as we would use a “regular” URL
  • However, if the picture is large and the color level of the picture is relatively rich, it is not suitable to use this method, becauseBase64It turns three bytes into four bytes, so the encoded text is about a third larger than the original text and it slows down the load speed.

Like:

<img src="data:image/png; base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mA biwWtuf0uxFAgA7">Copy the code

We use a built-in FileReader object to convert the Blob to Base64. It can read data in a Blob in a variety of formats

Here’s how to download blobs in Base64:

let link = document.createElement('a'); link.download = 'hello.txt'; let blob = new Blob(['Hello, world!'], {type: 'text/plain'}); let reader = new FileReader(); reader.readAsDataURL(blob); // Convert Blob to base64 and call onload reader.onload = function() {link.href = reader.result; // data url link.click(); }Copy the code
Image is converted to bloB
  • We can create an image, a portion of an image, or even a screenshot of a pageBlob. This makes it easy to upload to other places
  • Image manipulation is throughcanvasElement
  • usecanvas.drawImagecanvasDraw image on
  • callcanvasmethods.toBlob(callback, format, quality)To create aBlobAnd run with it after it is createdcallback.
// Get any image let img = document.querySelector('img'); <canvas> let canvas = document.createElement('canvas'); canvas.width = img.clientWidth; canvas.height = img.clientHeight; let context = canvas.getContext('2d'); Context. DrawImage (img, 0, 0); Function (blob) {// We context.rotate() and do a lot of other things on the canvas. Let link = document.createElement('a'); link.download = 'example.png'; link.href = URL.createObjectURL(blob); link.click(); RevokeObjectURL (link.href); // Delete the internal blob reference so that the browser can clear it from memory. }, 'image/png');Copy the code
Blobs are converted to arrayBuffers
// Get fileReader from blob let fileReader = new fileReader (); fileReader.readAsArrayBuffer(blob); fileReader.onload = function(event) { let arrayBuffer = fileReader.result; };Copy the code

File

  • FileThe name si means “document” and, generally speaking, it means we usefilecontrols<input type="file">Select theFileListObject, or using a drag-and-drop operationDataTransferobject
  • Here,FileObject is also binary object, based onBlobIt also extends file system-related functionalityFile
  • Inherited fromBlobObject,BlobObject properties and methods, and provides the name, lastModifiedDate, size, type and other basic metadata

There are two ways to get it

The first, similar to Blob, has a constructor:

new File(fileParts, fileName, [options])
Copy the code

Second, and more commonly, we get the file from or drag and drop or some other browser interface. In this case, file will get its this information from the operating system (OS)

<input type="file" onchange="showFile(this)"> <script> function showFile(input) { let file = input.files[0]; alert(`File name: ${file.name}`); // 115b.... -zoom-1.png alert(`Last modified: ${file.lastModified}`); // 1624268059721 } </script>Copy the code

FileReader

  • FileReaderIs an object whose sole purpose is to beBlob(also fromFile) object to read data
  • It uses events to pass data, since reading data from disk can be time-consuming

There is a constructor:

let reader = new FileReader(); // No argumentsCopy the code

Main method (to convert bloBs to other formats) :

  • readAsArrayBuffer(blob)Read data in binary formatArrayBuffer
  • readAsText(blob, [encoding])— Reads data into the given encoding (defaultutf-8Encoded) text string
  • readAsDataURL(blob)— Reads binary data and encodes it asbase64data url
  • abort()— Cancel operation

FileReader reads not only files, but also any Blob object

The choice of the read* method depends on which format we prefer and how we use the data

  • readAsArrayBuffer— For binary files, performing low-level binary operations. For things like slicing (slicing) and so on,FileIs inherited fromBlobSo we can call them directly without reading them
  • readAsText— For text files, when we want to get strings
  • readAsDataURLWhen we want to use this data in SRC and use it for img or other tags. As we did in theBlobThere is an alternative to reading files for this, as described in chapter 1:URL.createObjectURL(file)

During reading, the following events occur:

  • loadstart— Start loading
  • progress— occurs during reading
  • load— Read complete, no error
  • abort— Call abort()
  • error– appear error
  • loadendThe read completes, whether it succeeds or fails

After the read is complete, we can access the read results in the following ways:

  • reader.resultIs the result (if successful)
  • reader.errorYes error (if failed)

The most widely used events are undoubtedly Load and error

<input type="file" onchange="readFile(this)">

<script>
  function readFile(input) {
    let file = input.files[0];

    let reader = new FileReader();

    reader.readAsText(file);

    reader.onload = function() {
      console.log(reader.result);
    };

    reader.onerror = function() {
      console.log(reader.error);
    }
  }
</script>
Copy the code

Use a picture from the Internet to illustrate the relationship between them

Related information:

zh.javascript.info/formdata

Useful. Javascript. The info/arraybuffer…

zh.javascript.info/blob

zh.javascript.info/file

Learn the whole process of file uploading

Front-end large file upload

Bytedance Interviewer: Please implement a large file upload and resumable breakpoint

File upload. It’s enough to understand these 8 scenarios