Vue + element UI + springboot realizes the display function of file download progress bar

Vue + element UI + springboot realizes the display function of file download progress bar

Final effect drawing

1. Demand background

Recently, I received an optimization request. The original system file download function experience is not friendly, especially downloading some time-consuming files. Users are silly on the page and don't know what the download progress is. They always think that the system is stuck.

2. Optimization scheme

  1. Optimize the download speed in the background (you can study the piecemeal download, which is not expanded here)
  2. Transform the front-end user experience (for example, after clicking download, you should display the progress to let the customer know that it is already downloading)

3. Concrete realization

The scheme in 2.2 is selected here to transform the front-end user experience. The purpose of writing this article is to record the solution process at that time, hoping to help you; The technical background of the scheme used in this paper: front-end Vue + element UI, background: the front and back ends of spring boot are separated, there is no more nonsense, and the code is written directly;

3.1 front end code

1. Define a pop-up layer (the style is determined according to your preferences)

<!--Download progress bar-->
    <el-dialog title="Downloading, please wait" :visible.sync="fileDown.loadDialogStatus" :close-on-click-modal="false" 
      :close-on-press-escape="false" :show-close="false" width="20%">
      <div style="text-align: center;">
        <el-progress type="circle" :percentage="fileDown.percentage"></el-progress>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="downClose">Cancel Download</el-button>
      </div>  
    </el-dialog>
  1. Define an object in data()
fileDown: {
        loadDialogStatus: false, //Status of pop-up control
        percentage: 0, //Percentage of progress bar
        source: {}, //Resource object at cancel Download
      },

3. Main methods (pay attention to replacing the following parameters, background address, file name, etc.)

downFile(row) {
    //Put the parameters here
    var param = {};
    this.fileDown.loadDialogStatus = true;
    this.fileDown.percentage = 0;
    const instance = this.initInstance();
    instance({
        method: "post",
        withCredentials: true,
        url: "Replace download address",
        params: param,
        responseType: "blob"
    }).then(res => {
        this.fileDown.loadDialogStatus = false;
        console.info(res);
        const content = res.data;
        if (content.size == 0) {
          this.loadClose();
          this.$alert("Download failed");
          return ;
        }
        const blob = new Blob([content]);
        const fileName = row.fileName;//replace filename
        if ("download" in document.createElement("a")) {
          // Non IE Download
          const elink = document.createElement("a");
          elink.download = fileName;
          elink.style.display = "none";
          elink.href = URL.createObjectURL(blob);
          document.body.appendChild(elink);
          elink.click();
          setTimeout(function() {
            URL.revokeObjectURL(elink.href); // Release URL object
            document.body.removeChild(elink);
          }, 100);
        } else {
          // IE10 + download
          navigator.msSaveBlob(blob, fileName);
        }
      }).catch(error => {
          this.fileDown.loadDialogStatus = false;
          console.info(error);
      });
  },
initInstance() {
      var _this = this;
      //Resource tag at cancel
      this.fileDown.source = axios.CancelToken.source();
      const instance = axios.create({ //axios object should be imported in advance or replaced with your global definition
        onDownloadProgress: function(ProgressEvent) {
          const load = ProgressEvent.loaded;
          const total = ProgressEvent.total;
          const progress = (load / total) * 100;
          console.log('progress='+progress);
          //It's been calculated at the beginning. It can't continue until it exceeds the previous calculation
          if (progress > _this.fileDown.percentage) {
            _this.fileDown.percentage = Math.floor(progress);
          }
          if(progress == 100){
            //Download complete
            _this.fileDown.loadDialogStatus = false;
          }
        },
        cancelToken: this.fileDown.source.token,//Declare a cancel request token
      });
      return instance;
    },
    downClose() {
      //Interrupt Download
      this.$confirm("Clicking close will interrupt the download. Are you sure you want to close it?", this.$t("button.tip"), {
        confirmButtonText: this.$t("button.confirm"),
        cancelButtonText: this.$t("button.cancel"),
        type: "warning"
      }).then(() => {
          //Interrupt download callback
          this.fileDown.source.cancel('log==Customer manually cancels Download');
      }).catch(() => {
          //Cancel -- do nothing
      });      
    },
3.2 background code

The background is mainly to return the calculated file size, otherwise the total taken by the front end when calculating the progress is always 0, which is a hidden pit.
Key code: (download the complete background. There are actually many on the Internet. Here are just the key points and points needing attention)

//Get the local file and calculate the size
File file = new File(zipFileName);//Read compressed file
InputStream inputStream = new FileInputStream(file);
int totalSize = inputStream.available(); //Get file size
logger.info("After compression===Current file download size size={}", totalSize);
response.setHeader("Content-Length", totalSize+"");//Note here that the setHeader property must be set before response.getOutputStream(), otherwise it will not take effect
OutputStream out = response.getOutputStream();
Subsequent omission.....

4. Summary

  1. You may also encounter a problem in the process of using it, that is, it takes a lot of time for the back-end to calculate the file size, resulting in the front-end not moving for half a day, and users will still feel stuck, which will not meet our demands;
    My solution here is to make a timer at the front end. When you click download, the timer runs first. For example, increase the progress by 1% in 2 seconds. When the total file size is returned from the background, when the calculated percentage exceeds the percentage of the timer, turn off the timer and replace the attribute of the progress percentage ; Remember that this timer automatically increases the percentage. You must set an upper limit.
    The advantage is that when the user clicks the download button, the front end will give a response. Although the previous response may be false, as long as the connection is good, it doesn't matter whether it is true or false
    2. Finally, I write here. Everyone has a good plan. Welcome to comment and exchange. If you like, you can order three companies. I wish you fewer and fewer bugs and can be promoted and raised!!

Tags: Java Spring Boot Vue.js UI

Posted on Wed, 27 Oct 2021 20:53:14 -0400 by meomike2000