Front-end development is always inevitable about the file upload, download requirements. Here is a summary of common methods, welcome to discuss and ridicule.

Form submission

The most traditional way to upload a file is to upload a file using a form form. Just set encType to multipart/form-data. This way to upload files does not need JS, and there is no compatibility problem, all browsers support, but the experience is poor, resulting in page refresh, page other data loss.

<form method="post" action="xxxxx" enctype="multipart/form-data">Select file:<input type="file" name="file" />
  <br />Title:<input type="text" name="title" />
  <br />
  <button type="submit">submit</button>
</form>
Copy the code

Note:inputYou must set upnameProperty, or data cannot be sent

File interface upload

In this method, the server provides the interface, sets the corresponding request header, and submits the file data in the form of formData.

<input id="uploadFile" type="file" name="file" accept="image/png,image/gif" />
Copy the code
  • accept: indicates the MIME types of files that can be selected. Multiple MIME types are separated by commas (,)
  • multiple: Indicates whether multiple files can be selected
$('#uploadFile').on('change'.function (e) {
  var file = this.files[0]

  var formData = new FormData()
  formData.append('file', file)

  $.ajax({
    url: 'xxxx'.type: 'post'.data: formData,
    cache: false.contentType: false.processData: false.success: function (res) {
      //}})})Copy the code
  • processDataSet to false. Because the data value isFormDataObject that does not require data processing.
  • cacheIf this parameter is set to false, uploading files does not require caching.
  • contentTypeSet to false.

Shard to upload

Sometimes the files we upload may be very large, for example, videos may reach 2 GIGABytes, which will cause the upload speed to be too slow, and even the link times out sometimes. In addition, the server sometimes sets the size of files that are allowed to be uploaded, and files that are too large are not allowed to be uploaded. To solve this problem, we can upload files in fragments, uploading only a small part of the file such as 1 MB at a time.

Train of thought

  1. Slice a file into a small piece of a certain size (for example, 1M) and hash the slice with a hash value for identification.
  2. Each slice file is concurrently submitted to the server, and the server saves information about each slice file.
  3. After the slice is uploaded, the server merges the slice according to the file id and deletes the slice file after the merge.

In this way, since each slice is uploaded concurrently, the upload time can be effectively reduced. Here are the specific implementation steps. (PS: This is our way of implementation, not the only way, and the code related to the specific interface will not be posted here)

Generate the hash value

To upload file information or slice files, hash must be generated for the file and slice. The simplest hash value can be identified by the file name + subscript, but changing the file name will have no effect. In fact, as long as the file content is unchanged, the hash should not change, so the correct method is to generate the hash based on the file content. Our company uses the Spark-MD5 library, which will not be detailed here.

Uploading File Information

Before a file fragment is uploaded, information about the entire file, such as the total file size, file name, hash value, etc. is required to initialize a file fragment upload event and return the file ID for submission of each fragment.

getFileId (file) {
  let vm = this
  let formData = new FormData()
  formData.append('file', file)
  axios({
    timeout: 5 * 60 * 1000.headers: {
      'Content-Type': 'application/json-'.'x-data': JSON.stringify({
        fileName: file.fileName,
        size: file.size,
        hash: 'hashxxx',})},url: 'xxxxxx'.method: 'POST',
  })
  .then((res) = > {
    if (res.code === '200') {
      return res.data.fileId
    })
  .catch((err) = > {
    console.log(err)
  })
}
Copy the code

File slice segmentation

Slice method (similar to the slice method of array) is used to slice the large file by 1 MB, return a slice of the original file, and then upload each slice to the server.

getCkunk (file, fileId) {
  let vm = this
  let chunkSize = 1024 * 1024
  let totalSize = file.size
  let count = Math.ceil(totalSize / chunkSize)
  let chunkArr = []
  for (let i = 0; i < count; i++) {
    if (i === count.length - 1) {
      chunkArr.push(file.slice(i * chunkSize, totalSize))
    } else {
      chunkArr.push(file.slice(i * chunkSize, (i + 1) * chunkSize))
    }

  for (let index = 0; index < count; index++) {
    let item = chunkArr[index]
    this.uploadChunk(item, index, fileId)
  }
}
Copy the code

Method of uploading each shard to the server. Omit the hash value method here.

 ploadChunk(item, index, fileId) {
   let formData = new FormData()
   formData.append('file', item)
   request({
     headers: {
       'Content-Type': 'application/octet-stream; '.'x-data': JSON.stringify({
         fileId: fileId,
         partId: index + 1.hash: res,
       })
     },
     url: 'xxxxx'.method: 'POST'.data: formData,
   })
   .then((res) = > {
     return res.data.path
   })
   .catch((err) = > {
     console.log(err)
   })
 }
Copy the code

The upload progress bar is displayed

Due to the large size of the file, it takes a certain amount of time even to upload the file in fragments. For better user experience, it is best to prompt the upload progress in the front end. This requires the back end to add the uploaded 100% field to the return result of each shard. The front end gets the return value and changes the current progress.

When the last fragment is uploaded, the server returns the URL of the file, and the front end obtains the URL and changes the status of the progress bar to 100%.

Breakpoint continuingly

The above mentioned shard upload, to solve the large file upload timeout and server restrictions. However, for larger files, the upload is not completed in a short period of time, and sometimes even faced with network interruption or manual suspension, do we have to upload the whole file again, we certainly do not want to. This is where snap-on comes in handy.

Here is the implementation idea. First of all, breakpoint uploads must be based on shard uploads

  1. When each fragment is uploaded, the server records the hash value of the uploaded file and returns the hash value to the front-end, which records the hash value
  2. During re-uploading, the hash value of each file is compared with the hash value of the record. If the hash value is the same, the file is skipped and the next segment is uploaded.
  3. After all fragments are uploaded, the server merges them according to the file id and deletes the small files.

File download

There are several ways to download files

Form submission

This is the original method of adding a click event to a download button, dynamically generating a form when clicked, and using the form submission functionality to download the file (the form submission is actually sending a request).

function downloadFile(downloadUrl, fileName) {
  // Create form
  let form = document.createElement('form')
  form.method = 'get'
  form.action = downloadUrl
  //form.target = '_blank'; // Form opens a new page
  document.body.appendChild(form)
  form.submit()
  document.body.removeChild(form)
}
Copy the code
  • Advantages: Good compatibility, no URL length limitation problem.
  • Disadvantages: Cannot know the download progress, cannot directly download the browser can directly preview the file type (such as TXT/PNG, etc.)

Window. The open, or the window. The location. The href

The simplest and most direct way is actually the same as a tag to access the download link

window.open('downloadFile.zip')
location.href = 'downloadFile.zip'
Copy the code

disadvantages

  • URL length limits occur
  • Pay attention to URL encoding
  • The types of files that can be directly viewed by the browser are not available for download, such as TXT, PNG, JPG, and GIF
  • If you cannot add headers, authentication cannot be performed
  • There is no way to know the download progress

A Tag Download attribute

Download attribute is a new attribute in HTML5, can I use Download.

<a href="xxxx" download>Click on the download</a>
<! -- Rename the downloaded file -->
<a href="xxxx" download="test">Click on the download</a>
Copy the code

Advantages: It can solve the problem of downloading browserable files directly.

disadvantages

  • You need to know where to download the file
  • Cannot download files that can be browsed by browsers across domains
  • Compatibility issues, especially IE
  • Authentication cannot be performed

Use Blob objects

In addition to downloading with a known file address path, this method can also download by sending an Ajax request API to fetch a file stream. Blob objects are used to stream files into Blob binary objects.

CreateObjectUrl to generate the URL address, assign the value to the href attribute of a tag, and download the binary data.

downdFile (path, name) {
  const xhr = new XMLHttpRequest();
  xhr.open('get', path);
  xhr.responseType = 'blob';
  xhr.send();
  xhr.onload = function () {
    if (this.status === 200 || this.status === 304) {
      // const blob = new Blob([this.response], { type: xhr.getResponseHeader('Content-Type') });
      // const url = URL.createObjectURL(blob);
      const url = URL.createObjectURL(this.response);
      const a = document.createElement('a');
      a.style.display = 'none';
      a.href = url;
      a.download = name;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a); URL.revokeObjectURL(url); }}}Copy the code

Recommend the article

Advanced javascript functions you may not know about
Summarize the way javascript handles asynchrony
Summary of mobile H5 development tips.
Build a WebPack project from scratch
Summary of several webpack optimization methods
Summarize the methods of front-end performance optimization
Summary of advanced application of VUE knowledge system
Summarize the practical skills of vUE knowledge system
Several common JS recursive algorithms
Encapsulate a TOAST and Dialog component and publish it to NPM