This is the 8th day of my participation in Gwen Challenge

Recently just do a requirement, about the file upload, download function, also encountered some pits in the middle, record summary, before writing this function, we need to understand the prior knowledge. The front-end sends a request with parameters, and the server responds to the request and receives parameters. Generally, the encoding mode of the message body is obtained according to the Content-Type field, and then decoded accordingly.

1, before the words,

In the earliest HTTP POST requests, parameters were passed through the browser URL and only supported: Application/X-www-form-urlencoded, which does not support file uploads, content-Type extends the multipart/form-data type to support sending binary data to the server, and later, The evolution of easy-going Web applications has added the Application/JSON type, the most commonly used, for RESTFUL interfaces

2, Content-Typetype

(1) application/x-www-form-urlencoded

For native forms, if encType attribute is not set, data will be submitted in application/ X-www-form-urlencoded mode by default, and the submitted expression data will be converted into key-value pairs. The URL is encoded in the key= Value1&key =value3&key=value3 format. If the URL contains Chinese characters or special symbols, the URL is automatically transcoded. Note: Files are not supported and are generally used for form submission

(2) multipart/form-data

When a form uploads a file, it must set the encType of the form to be equal to the type multipart/form-data. This method is mostly used for file uploading. Boundary is used to separate each form item. Boundary boundary is generally placed after content-Type and transmitted to the server, and the server analyzes data and divides segments according to this boundary.

Connection: keep-alive
Content-Length: 9020
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryujJLO5p0jTkaNNbC
Copy the code

Note: Content-Type indicates that the data is encoded by mutipart/form-data. What is the Content of boundary in this request? The message body is divided into several parts with similar structure according to the number of fields. Each part starts with boundary, and the message body ends with boundary marking

<form action="/" method="post" enctype="multipart/form-data">
  <input type="text" name="description">
  <input type="file" name="myFile">
  <button type="submit">Submit</button>
</form>
Copy the code

(3) application/json

One of the benefits of the JSON format is that it supports structured data that is much more complex than key-value pairs, and the server can handle JSON data very well. Due to the popularity of the JSON specification, Major browsers are starting to support json.stringfy natively. Most RESTFUL apis receive application/ JSON types,

3. Upload and download

This requirement is special: the back end will return me a different data structure. If the file is uploaded successfully and the back end verifies successfully, the interface will return me the following data: JSON, if the file is uploaded successfully, the back end verification fails, the interface returns my data as a binary stream, the front end needs to download it in.xlsx mode, if the file fails to upload, the interface will return a binary stream, if the parsing fails, it will also give an error message

The first step is to download the Excel template, which is relatively easy:

1. Obtain template data from the server:

responseType: 'blob',
Copy the code

2, then encapsulate a download method

/** * description: the first parameter of the blob object is response.data, which represents the file stream returned by the back end. The second parameter sets the file type * response: blob object data * filename: filename */
downloadFileExport(response, filename) {
      const contentType = response.type // File type
      const blob = new Blob([response], {
        type: contentType
      })
      if ('download' in document.createElement('a')) { // Non-IE download
           const linkElement = document.createElement('a')  // Create a tag
        // Generate download link, this link is directly downloaded on the A tag, put on the IMG can directly display the image price, the same as the video
           const url = window.URL.createObjectURL(blob)
           linkElement.setAttribute('href', url)
           linkElement.setAttribute('target'.'_blank')
           linkElement.setAttribute('download', filename)
          // Simulate clicking the A TAB
        if (typeof MouseEvent === 'function') {
            var event = new MouseEvent('click', {
                view: window.bubbles: true.cancelable: false
           })
           linkElement.dispatchEvent(event)
        }
      } else {
        navigator.msSaveBlob(blob, filename)
      }
    }
Copy the code

3. Template download

Data: Blob data stream retrieved from the back end,const textValue = 'User form.xlsx'
        this.downLoadFileExport(data, textValue)
Copy the code

4. File upload

1. The first step is to verify the uploaded files

// Click the download button,
changeFile(file) {
   const fileData = file.raw // Get the file contents
   if(! fileData)return; // If the file content is empty
    // 1
    CheckExcel (param) checkExcel(param) checkExcel(param)
    // 3, according to the verification results to display
    checkExcel(form).then(res= > {
        let data = res.data // Get the bloB data stream of the validation result returned by the server
        // If the type returned by the backend is JSON, there is no need to download Excel
        if(data.type.includes('application/json')) { 
            var reader = new FileReader(); // FileReader converts the blod stream into a JSON object
            reader.onload = (e) = > { // Async function to retrieve the JOSN object
              let result = JSON.parse(reader.result);
              if (result && result.code == 200) { 
                  // Let the successfully uploaded file be displayed, and only one can be uploaded
                  this.fileList[0] = {name: file.name, uid: file.uid }                   
                  this.$message.success('File uploaded successfully')}else {
                  this.fileList = []
                  this.$message.error(result.message)
              }
            }
            reader.readAsText(data, 'utf-8');
         }
         // If the type returned by the backend is Application /vnd.ms-excel, excel needs to be downloaded
         if(data.type.includes('application/vnd.ms-excel')) { // For excel streams
            this.fileList = []
            this.$message.error('File upload failed')
            const textValue = file.name
            const blob = new Blob([res.data], {type: "application/vnd.ms-excel; charset=utf-8"})
            this.downLoadFileExport(blob, textValue)
        } 
      }).catch(err= > {
        this.fileList = []
        console.log(err)
      })
    
}
Copy the code

2. The second step is the verification of the document content approval chain

/** * Verify file contents */
checkContent(params).then(res= > {
          let checkData = res.data // The backend returns the result of the server verifying the file
          // If the result is a JSON object,
          if(checkData.type.includes('application/json')) {
           var reader = new FileReader();
              reader.onload =(e) = > {
              let result = JSON.parse(reader.result);
                if (result.data && result.code == '200') {
                    this.approveChainForSubmit = result.data.approvalChain
                    this.cacheKey = result.data.cacheKey
                    // Other operations after the data is obtained can be ignored
                  } else {
                    this.$message.error(result.message)
                  }
              }
              reader.readAsText(checkData, 'utf-8'); // Pay attention to the order
           }
           // If application/vnd.ms-excel is returned from the backend, the verification fails and you need to download Excel
           if(checkData.type.includes('application/vnd.ms-excel')) {
              this.$message.error('Approval chain validation failed')
              const textValue = this.fileList[0].name
              const blob = new Blob([checkData], {type: "application/vnd.ms-excel; charset=utf-8"})
               this.downLoadFileExport(blob, textValue)
               return new Promise(() = >{}) // Terminate the chain call to. Then, which returns only a pendding promise
        }
        }).catch(err= > {
        console.log(err)
      })
Copy the code

4, step on the pit

Here is one of three needs:

1. Event sequence of fileReader objects

ReadAsText (checkData, ‘utF-8 ‘) and read. onload =(e) =>{

Reader.onload

After: reader. ReadAsText

The desired method must be executed after reader.onload to fetch the desired parameter

2, return new Promise(() =>{})

Multiple chain requests, only to one error, to terminate. A chained call to terminate. Then has one and only method that returns a Pendding state promise

3, blob = new blob ([data], filename)

A BLOB, or binary large object, is a container that can store binary files,

A Blob object refers to a sequence of bytes and has the size attribute, which is the total number of bytes in the sequence, and a Type attribute, which is a sequence of media-type bytes represented by a lowercase ASCII encoded string.

Create using the constructor of Blob(). The constructor takes two arguments

The first argument is a sequence of data in the format ArrayBuffer, ArrayBufferView, Blob, or DOMString. The second argument is an object containing the following two propertiesCopy the code

5, DOM code

A quick post on the DOM code

<el-button class="downLoad" @click="downloadTemplate">Download the template</el-button>
            <div class="ElFormItem-right">
              <el-upload
                :auto-upload="false"
                :show-file-list="true"
                :on-change="changeFile" 
                :action="upload"
                :file-list="fileList"
                :on-remove="handleRemove"
                accept=".xlsx"
              >
              <el-button size="mini">Batch upload</el-button>
              </el-upload>
            </div>
Copy the code

6,

There are many ways to download it, you can also use a third party to work. This article is mainly to record the problems I encountered during the development project. The main problem is that I took a lot of time to step on the pit, the order is not right, the asynchronous events of reader.onload keep going in