溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何實(shí)現(xiàn)springboot帶有進(jìn)度條的上傳功能

發(fā)布時(shí)間:2021-09-27 15:32:47 來(lái)源:億速云 閱讀:108 作者:小新 欄目:編程語(yǔ)言

這篇文章將為大家詳細(xì)講解有關(guān)如何實(shí)現(xiàn)springboot帶有進(jìn)度條的上傳功能,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

具體如下:

一、說(shuō)明

  最近要做文件上傳,在網(wǎng)上找了很久都沒(méi)有一個(gè)全面的示例,特此記錄下來(lái)分享給大家。

  1.文件上傳接口可按照springboot默認(rèn)實(shí)現(xiàn),也可用commons-fileupload組件,本示例使用springboot默認(rèn)文件上傳 2.最后也有commons-fileupload組件接口示例

  2.重點(diǎn)在前端JS實(shí)現(xiàn)(也可以使用ajax上傳),參考了網(wǎng)上大量上傳文件顯示進(jìn)度條博客以及技術(shù)方案,在此做了一個(gè)統(tǒng)一的總結(jié),方便后續(xù)使用

  3.這僅僅是一個(gè)示例,大家可根據(jù)實(shí)際需要改進(jìn)。

二、前端代碼

<!DOCTYPE html><html><meta charset="UTF-8" /><head><title>文件上傳</title><link href="https://cdn.bootcss.com/bootstrap/3.3.2/css/bootstrap.css" rel="external nofollow"   rel="stylesheet"><script src="http://code.jquery.com/jquery-1.10.2.min.js"></script></head><body class="container">  <br />  <span id="time"></span>  <p class="row">    <input class="btn btn-info btn-xs" type="file" name="file" /><br />    <p class="col-lg-5"      >      <p class="progress progress-striped active" >        <p id="progressBar" class="progress-bar progress-bar-success"          role="progressbar" aria-valuemin="0" aria-valuenow="0"          aria-valuemax="100" ></p>      </p>    </p>    <!-- 顯示上傳速度 -->    <p id="showInfo" class="col-lg-2">0KB/s</p>  </p>  <!-- 顯示文件信息 -->  <p id="showFieInfo" class="row">    <label name="upfileName"></label><br />     <label name="upfileSize"></label><br />    <label name="upfileType"></label><br />  </p>  <p class="row">    <input class="btn btn-success btn-xs" type="button" name="upload" value="上傳" />    <input class="btn btn-success btn-xs" type="button" name="cancelUpload" value="取消" />  </p></body><script type="text/javascript">  var fileBtn = $("input[name=file]");  var processBar= $("#progressBar");  var uploadBtn=$("input[name=upload]");  var canelBtn=$("input[name=cancelUpload]");  var ot;//上傳開(kāi)始時(shí)間  var oloaded;//已上傳文件大小  fileBtn.change(function() {    var fileObj = fileBtn.get(0).files[0]; //js獲取文件對(duì)象    if (fileObj) {      var fileSize = getSize(fileObj.size);      $("label[name=upfileName]").text('文件名:' + fileObj.name);      $("label[name=upfileSize]").text('文件大小:' + fileSize);      $("label[name=upfileType]").text('文件類型:' + fileObj.type);      uploadBtn.attr('disabled', false);    }  });  // 上傳文件按鈕點(diǎn)擊的時(shí)候  uploadBtn.click(function(){    // 進(jìn)度條歸零    setProgress(0);    // 上傳按鈕禁用    $(this).attr('disabled', true);    // 進(jìn)度條顯示    showProgress();    // 上傳文件    uploadFile();  });  function uploadFile(){    var url ="/to/upload";    var fileObj = fileBtn.get(0).files[0];    if(fileObj==null){      alert("請(qǐng)選擇文件");      return;    }    // FormData 對(duì)象    var form = new FormData();    form.append('file', fileObj); // 文件對(duì)象    // XMLHttpRequest 對(duì)象    var xhr = new XMLHttpRequest();    //true為異步處理    xhr.open('post', url, true);    //上傳開(kāi)始執(zhí)行方法    xhr.onloadstart = function() {       console.log('開(kāi)始上傳')       ot = new Date().getTime();  //設(shè)置上傳開(kāi)始時(shí)間       oloaded = 0;//已上傳的文件大小為0    };    xhr.upload.addEventListener('progress', progressFunction, false);    xhr.addEventListener("load", uploadComplete, false);    xhr.addEventListener("error", uploadFailed, false);    xhr.addEventListener("abort", uploadCanceled, false);    xhr.send(form);    function progressFunction(evt) {      debugger;      if (evt.lengthComputable) {        var completePercent = Math.round(evt.loaded / evt.total * 100)            + '%';        processBar.width(completePercent);        processBar.text(completePercent);        var time = $("#time");        var nt = new Date().getTime();   //獲取當(dāng)前時(shí)間        var pertime = (nt-ot)/1000;    //計(jì)算出上次調(diào)用該方法時(shí)到現(xiàn)在的時(shí)間差,單位為s        ot = new Date().getTime();     //重新賦值時(shí)間,用于下次計(jì)算        var perload = evt.loaded - oloaded; //計(jì)算該分段上傳的文件大小,單位b            oloaded = evt.loaded;        //重新賦值已上傳文件大小        //上傳速度計(jì)算        var speed = perload/pertime;//單位b/s        var bspeed = speed;        var units = 'b/s';//單位名稱        if(speed/1024>1){          speed = speed/1024;          units = 'k/s';        }        if(speed/1024>1){          speed = speed/1024;          units = 'M/s';        }        speed = speed.toFixed(1);        //剩余時(shí)間        var resttime = ((evt.total-evt.loaded)/bspeed).toFixed(1);        $("#showInfo").html(speed+units+',剩余時(shí)間:'+resttime+'s');      }    }    //上傳成功后回調(diào)                                     function uploadComplete(evt) {      uploadBtn.attr('disabled', false);      console.log('上傳完成')    };    //上傳失敗回調(diào)          function uploadFailed(evt) {      console.log('上傳失敗' + evt.target.responseText);    }    //終止上傳       function cancelUpload() {      xhr.abort();    }    //上傳取消后回調(diào)           function uploadCanceled(evt) {      console.log('上傳取消,上傳被用戶取消或者瀏覽器斷開(kāi)連接:' + evt.target.responseText);    }    canelBtn.click(function(){      uploadBtn.attr('disabled', false);      cancelUpload();    })  }  function getSize(size) {    var fileSize = '0KB';    if (size > 1024 * 1024) {      fileSize = (Math.round(size / (1024 * 1024))).toString() + 'MB';    } else {      fileSize = (Math.round(size / 1024)).toString() + 'KB';    }    return fileSize;  }  function setProgress(w) {    processBar.width(w + '%');  }  function showProgress() {    processBar.parent().show();  }  function hideProgress() {    processBar.parent().hide();  }</script></html>

三、對(duì)上傳代碼進(jìn)行組件化封裝

UploadCommon.js

/** * 上傳文件公共組件 *  * @param url 上傳地址 * @param processBar 進(jìn)度條 jquery獲取的頁(yè)面組件 * @param speedLab 顯示上傳速度Label jquery獲取的頁(yè)面組件 * @param uploadBtn 上傳按鈕 jquery獲取的頁(yè)面組件 * @param cancelBtn 取消上傳按鈕 jquery獲取的頁(yè)面組件 * @param callBack 上傳完成回調(diào)函數(shù) 上傳完成后的回調(diào)函數(shù),可以不傳 * @author * @returns */function UploadCommon(url, processBar, speedLab, uploadBtn, cancelBtn, callBack){  function init() {    // 每次回調(diào)監(jiān)測(cè)上傳開(kāi)始時(shí)間    var startTime = null    // 已上傳文件大小    var oloaded = null     var xhr = new XMLHttpRequest()    function setProgress(w) {      processBar.width(w + '%');      processBar.text(w + '%');    }    function progressMonitor(evt){      if (evt.lengthComputable) {        var completePercent = Math.round(evt.loaded / evt.total * 100)        setProgress(completePercent)        var nowTime = new Date().getTime()        // 計(jì)算出上次調(diào)用該方法時(shí)到現(xiàn)在的時(shí)間差,單位為s        var pertime = (nowTime - startTime) / 1000           // 重新賦值時(shí)間,用于下次計(jì)算        startTime = new Date().getTime()        // 計(jì)算該分段上傳的文件大小,單位b        var perload = evt.loaded - oloaded        // 重新賦值已上傳文件大小,用以下次計(jì)算        oloaded = evt.loaded        // 上傳速度計(jì)算,單位b/s        var speed = perload / pertime        var bspeed = speed        // 單位名稱        var units = 'bit/s'if (speed / 1024 > 1) {          speed = speed / 1024          units = 'Kb/s'        }        if (speed / 1024 > 1) {          speed = speed / 1024          units = 'Mb/s'        }        speed = speed.toFixed(1);        // 剩余時(shí)間        var resttime = ((evt.total - evt.loaded) / bspeed).toFixed(1)        speedLab.html(speed + units + ',剩余時(shí)間:' + resttime + 's')      }    }    // 上傳成功后回調(diào)    function uploadComplete(evt) {      uploadBtn.attr('disabled', false)      var status = evt.currentTarget.status      if (status == 401) {        alert('請(qǐng)登錄后再上傳')        return      }      if (status == 403) {        alert('無(wú)權(quán)限操作')        return      }      if (status != 200) {        alert('上傳異常,錯(cuò)誤碼' + status)        return      }      var response=JSON.parse(evt.currentTarget.response)      if (response.code!='200') {        alert('上傳處理異常' + response.msg)        return      }      console.log('上傳成功')      if ( callBack != null && typeof callBack != 'undefined') {        callBack()      }    };    // 上傳失敗回調(diào)    function uploadFailed(evt) {      alert('上傳處理失敗' + evt.target.responseText)    }    // 終止上傳    function cancelUpload() {      xhr.abort()    }    // 上傳取消后回調(diào)    function uploadCanceled(evt) {      alert('文件上傳已終止:' + evt.target.responseText)    }    // 添加取消上傳事件    cancelBtn.click(function() {          uploadBtn.attr('disabled', false)          cancelUpload();        })    this.uploadFile = function(formData) {      // 上傳按鈕禁用      uploadBtn.attr('disabled', true);      setProgress(0)      // true為異步處理      xhr.open('post', url, true)      // 上傳開(kāi)始執(zhí)行方法      xhr.onloadstart = function() {        console.log('開(kāi)始上傳')        // 設(shè)置上傳開(kāi)始時(shí)間        startTime = new Date().getTime()         // 已上傳的文件大小為0        oloaded = 0       }      xhr.upload.addEventListener('progress', progressMonitor, false)      xhr.addEventListener("load", uploadComplete, false)      xhr.addEventListener("error", uploadFailed, false)      xhr.addEventListener("abort", uploadCanceled, false)      xhr.send(formData);    }    this.setProgressValue=function(w){      processBar.width(w + '%')      processBar.text(w + '%')    }  }  return new init()}

調(diào)用

$(document).ready(function() {  var addVersionBtn=$('#addVersionBtn') //<button>  var cancelUploadBtn=$('#cancelUploadBtn') //<button>  var fileInput=$("#filewareFile") //<input>  var processBar = $("#progressBar"); //p  var fileNameLab=$("label[name=upfileName]") //<label>  var fileSizeLab=$("label[name=upfileSize]") //...  var fileTypeLab=$("label[name=upfileType]") //...  var speedLab=$("#showSpeed") //<label>  var url='/api/file'  //獲取文件上傳實(shí)例  var upload=UploadCommon(url,processBar,speedLab,addVersionBtn,cancelUploadBtn,initPageInfo)  // 文件選擇框變更事件  fileInput.change(function() {    var fileObj = fileInput.get(0).files[0]; // js獲取文件對(duì)象    if (fileObj) {      var fileSize = getSize(fileObj.size);      fileNameLab.text('文件名:' + fileObj.name);      fileSizeLab.text('文件大?。?#39; + fileSize);      fileTypeLab.text('文件類型:' + fileObj.type);      addVersionBtn.attr('disabled', false);    }  });  // 點(diǎn)擊上傳固件事件  addVersionBtn.click(function(){    var versionInfo=$('#version').val()    var file = fileInput.get(0).files[0]    var strategyInfo=$('#versionType').val()    if(file==null){      alert("固件文件不能為空")      return    }    if(versionInfo==''){      alert("版本號(hào)不能為空")      return    }    if(strategyInfo==''){      alert("升級(jí)策略不能為空")      return    }    // 創(chuàng)建提交數(shù)據(jù)    var formData = new FormData();    formData.append('firmFile', fileInput.get(0).files[0]);     formData.append('version', versionInfo);    formData.append('strategy', strategyInfo);     // 上傳文件    upload.uploadFile(formData)      })});

四,服務(wù)端接口

1.springboot默認(rèn)實(shí)現(xiàn)

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>com.demo</groupId>  <artifactId>demo</artifactId>  <version>0.0.1-SNAPSHOT</version>  <parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>1.5.10.RELEASE</version>    <relativePath/>  </parent>  <properties>    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>    <java.version>1.8</java.version>  </properties>  <dependencies>    <dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-devtools</artifactId>      <optional>true</optional>    </dependency>    <dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-thymeleaf</artifactId>    </dependency>    <dependency>       <groupId>net.sourceforge.nekohtml</groupId>       <artifactId>nekohtml</artifactId>    </dependency>    <dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-test</artifactId>      <scope>test</scope>    </dependency>    <!-- 添加Swagger2依賴,用于生成接口文檔 -->    <dependency>      <groupId>io.springfox</groupId>      <artifactId>springfox-swagger2</artifactId>      <version>2.7.0</version>    </dependency>    <dependency>      <groupId>io.springfox</groupId>      <artifactId>springfox-swagger-ui</artifactId>      <version>2.7.0</version>    </dependency>    <!--end-->  </dependencies>  <build>    <plugins>      <plugin>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-maven-plugin</artifactId>      </plugin>    </plugins>  </build></project>

application.yml

server: port: 8080 tomcat:  uri-encoding: UTF-8 application:  name: demo thymeleaf:  encoding: UTF-8  cache: true  mode: LEGACYHTML5 devtools:  restart:   enabled: true http:  multipart:   maxFileSize: 500Mb   maxRequestSize: 500Mb   location: D:/tmpdebug: false

接口:

@PostMapping("/upload")public String uploadFile(@RequestParam("file") MultipartFile file) {    if (file == null || file.isEmpty()) {      return "file is empty";    }    // 獲取文件名    String fileName = file.getOriginalFilename();    // 文件存儲(chǔ)路徑    String filePath = "D:/data/" + UUID.randomUUID().toString().replaceAll("-", "") + "_" + fileName;    logger.info("save file to:" + filePath);    File dest = new File(filePath);    if (!dest.getParentFile().exists()) {      dest.getParentFile().mkdirs();    }    try {      file.transferTo(dest);      return "success";    } catch (Exception e) {      e.printStackTrace();    }    return "fail";  }

啟動(dòng)類

import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.builder.SpringApplicationBuilder;import org.springframework.boot.web.support.SpringBootServletInitializer;import org.springframework.transaction.annotation.EnableTransactionManagement;@SpringBootApplication@EnableTransactionManagementpublic class Application extends SpringBootServletInitializer {  @Override  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {    return application.sources(Application.class);  }  public static void main(String[] args) {    SpringApplication.run(Application.class, args);  }}

2.使用commons-fileupload上傳組件

application.yml

server:  port: 8080 tomcat:  uri-encoding : UTF-8spring:  application:   name: svc-demo thymeleaf:   encoding: UTF-8  cache: false  mode: LEGACYHTML5debug: false

pom .xml

<parent>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-parent</artifactId>   <version>1.5.10.RELEASE</version>   <relativePath/> </parent> <properties>   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>   <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>   <java.version>1.8</java.version> </properties> <dependencies>   <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-devtools</artifactId>     <optional>true</optional>   </dependency>   <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-web</artifactId>   </dependency>   <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-thymeleaf</artifactId>   </dependency>   <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-test</artifactId>     <scope>test</scope>   </dependency>   <!--添加文件上傳支持-->   <dependency>      <groupId>commons-io</groupId>      <artifactId>commons-io</artifactId>      <version>2.4</version>   </dependency>   <dependency>      <groupId>commons-fileupload</groupId>      <artifactId>commons-fileupload</artifactId>      <version>1.3.1</version>   </dependency>   <!--添加html5支持-->   <dependency>       <groupId>net.sourceforge.nekohtml</groupId>       <artifactId>nekohtml</artifactId>   </dependency>  </dependencies> <build>   <plugins>     <plugin>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-maven-plugin</artifactId>     </plugin>   </plugins> </build>

進(jìn)程類

public class Progress{  private long bytesRead; //已讀取文件的比特?cái)?shù)  private long contentLength;//文件總比特?cái)?shù)  private long items; //正讀的第幾個(gè)文件  public long getBytesRead(){    return bytesRead;  }  public void setBytesRead(long bytesRead){    this.bytesRead = bytesRead;  }  public long getContentLength() {    return contentLength;  }  public void setContentLength(long contentLength) {    this.contentLength = contentLength;  }  public long getItems() {    return items;  }  public void setItems(long items)\{    this.items = items;  }}

監(jiān)聽(tīng)類

@Componentpublic class FileUploadProgressListener implements ProgressListener{  private HttpSession session;  public void setSession(HttpSession session){    this.session=session;    Progress status = new Progress();//保存上傳狀態(tài)    session.setAttribute("status", status);  }  @Override  public void update(long bytesRead, long contentLength, int items) {    Progress status = (Progress) session.getAttribute("status");    status.setBytesRead(bytesRead);    status.setContentLength(contentLength);    status.setItems(items);  }}

文件上傳處理類

public class CustomMultipartResolver extends CommonsMultipartResolver{  // 注入第二步寫(xiě)的FileUploadProgressListener  @Autowired  private FileUploadProgressListener progressListener;  public void setFileUploadProgressListener(FileUploadProgressListener progressListener){    this.progressListener = progressListener;  }  @Override  public MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException{    String encoding = determineEncoding(request);    FileUpload fileUpload = prepareFileUpload(encoding);     //fileUpload.setFileSizeMax(1024 * 1024 * 500);// 單個(gè)文件最大500M    //fileUpload.setSizeMax(1024 * 1024 * 500);// 一次提交總文件最大500M    progressListener.setSession(request.getSession());// 問(wèn)文件上傳進(jìn)度監(jiān)聽(tīng)器設(shè)置session用于存儲(chǔ)上傳進(jìn)度    fileUpload.setProgressListener(progressListener);// 將文件上傳進(jìn)度監(jiān)聽(tīng)器加入到 fileUpload 中    try{      List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);      return parseFileItems(fileItems, encoding);    } catch (FileUploadBase.SizeLimitExceededException ex) {      throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);    } catch (FileUploadException ex){      throw new MultipartException("Could not parse multipart servlet request", ex);    }  }}

控制器

@RestControllerpublic class FileController{  @PostMapping("/upload")  public String uploadFile(@RequestParam("file") MultipartFile file) {    if (file.isEmpty()) {      return "文件為空";     }    // 獲取文件名    String fileName = file.getOriginalFilename();// 文件上傳后的路徑    // 文件上傳后的路徑    String filePath = null;    try{      filePath = new File("").getCanonicalPath() + "/tmp/uploadFile/";    } catch (IOException e){      e.printStackTrace();    }    //存儲(chǔ)路徑    String tagFilePath = filePath + CommonUtil.getCurrentTime() + fileName;    File dest = new File(tagFilePath);    // 檢測(cè)是否存在目錄    if (!dest.getParentFile().exists()){      dest.getParentFile().mkdirs();    }    try{      file.transferTo(dest);    } catch (IllegalStateException e){      e.printStackTrace();    } catch (IOException e){      e.printStackTrace();    }    return fileName + "上傳失敗";  }}

啟動(dòng)類

//注意取消自動(dòng)Multipart配置,否則可能在上傳接口中拿不到file的值@EnableAutoConfiguration(exclude = { MultipartAutoConfiguration.class })@SpringBootApplicationpublic class Application extends SpringBootServletInitializer{  //注入自定義的文件上傳處理類  @Bean(name = "multipartResolver")  public MultipartResolver multipartResolver() {    CustomMultipartResolver customMultipartResolver = new CustomMultipartResolver();    return customMultipartResolver;  }  @Override  protected SpringApplicationBuilder configure(SpringApplicationBuilder application){    return application.sources(Application.class);  }  public static void main(String[] args) {    SpringApplication.run(Application.class, args);  }

關(guān)于“如何實(shí)現(xiàn)springboot帶有進(jìn)度條的上傳功能”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI