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
- Optimize the download speed in the background (you can study the piecemeal download, which is not expanded here)
- 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>
- 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
- 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!!