溫馨提示×

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

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

springboot中怎么實(shí)現(xiàn)切割分片上傳功能

發(fā)布時(shí)間:2021-07-23 16:55:29 來源:億速云 閱讀:127 作者:Leah 欄目:編程語言

本篇文章為大家展示了springboot中怎么實(shí)現(xiàn)切割分片上傳功能,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

文件上傳是web開發(fā)中經(jīng)常會(huì)遇到的

springboot的默認(rèn)配置為10MB,大于10M的是傳不上服務(wù)器的,需要修改默認(rèn)配置

但是如果修改支持大文件又會(huì)增加服務(wù)器的負(fù)擔(dān)。

當(dāng)文件大于一定程度時(shí),不僅服務(wù)器會(huì)占用大量?jī)?nèi)存,而且http傳輸極可能會(huì)中斷。

可以采用切割分片上傳

html5提供的文件API中可以輕松的對(duì)文件進(jìn)行分割切片,然后通過ajax異步處理向服務(wù)器傳輸數(shù)據(jù),突破對(duì)大文件上傳的限制,

同時(shí)異步處理在一定程度上也提高了文件上傳的效率。

過程描述:

將文件分割成N片  處理分片,前臺(tái)會(huì)多次調(diào)用上傳接口,每次都會(huì)上傳文件的一部分到服務(wù)端  N個(gè)分片都上傳完成后,將N個(gè)文件合并為一個(gè)文件,并將N個(gè)分片文件刪除

1.服務(wù)端

(1)添加依賴

<dependency>   <groupId>commons-fileupload</groupId>   <artifactId>commons-fileupload</artifactId>   <version>1.3.3</version></dependency>

(2)UploadController

package com.example.demo.controller;import com.example.demo.core.Result;import org.apache.commons.fileupload.servlet.ServletFileUpload;import org.apache.commons.io.FileUtils;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.CrossOrigin;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;@CrossOrigin@Controller@RequestMapping("/api/upload")public class UploadController {  @PostMapping("/part")  @ResponseBody  public Result bigFile(HttpServletRequest request, HttpServletResponse response, String guid, Integer chunk, MultipartFile file, Integer chunks) {    try {      String projectUrl = System.getProperty("user.dir").replaceAll("\\\\", "/");      ;      boolean isMultipart = ServletFileUpload.isMultipartContent(request);      if (isMultipart) {        if (chunk == null) chunk = 0;        // 臨時(shí)目錄用來存放所有分片文件        String tempFileDir = projectUrl + "/upload/" + guid;        File parentFileDir = new File(tempFileDir);        if (!parentFileDir.exists()) {          parentFileDir.mkdirs();        }        // 分片處理時(shí),前臺(tái)會(huì)多次調(diào)用上傳接口,每次都會(huì)上傳文件的一部分到后臺(tái)        File tempPartFile = new File(parentFileDir, guid + "_" + chunk + ".part");        FileUtils.copyInputStreamToFile(file.getInputStream(), tempPartFile);      }    } catch (Exception e) {      return Result.failMessage(400,e.getMessage());    }    return Result.successMessage(200,"上次成功");  }  @RequestMapping("merge")  @ResponseBody  public Result mergeFile(String guid, String fileName) {    // 得到 destTempFile 就是最終的文件    String projectUrl = System.getProperty("user.dir").replaceAll("\\\\", "/");    try {      String sname = fileName.substring(fileName.lastIndexOf("."));      //時(shí)間格式化格式      Date currentTime = new Date();      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");      //獲取當(dāng)前時(shí)間并作為時(shí)間戳      String timeStamp = simpleDateFormat.format(currentTime);      //拼接新的文件名      String newName = timeStamp + sname;      simpleDateFormat = new SimpleDateFormat("yyyyMM");      String path = projectUrl + "/upload/";      String tmp = simpleDateFormat.format(currentTime);      File parentFileDir = new File(path + guid);      if (parentFileDir.isDirectory()) {        File destTempFile = new File(path + tmp, newName);        if (!destTempFile.exists()) {          //先得到文件的上級(jí)目錄,并創(chuàng)建上級(jí)目錄,在創(chuàng)建文件          destTempFile.getParentFile().mkdir();          try {            destTempFile.createNewFile();          } catch (IOException e) {            e.printStackTrace();          }        }        for (int i = 0; i < parentFileDir.listFiles().length; i++) {          File partFile = new File(parentFileDir, guid + "_" + i + ".part");          FileOutputStream destTempfos = new FileOutputStream(destTempFile, true);          //遍歷"所有分片文件"到"最終文件"中          FileUtils.copyFile(partFile, destTempfos);          destTempfos.close();        }        // 刪除臨時(shí)目錄中的分片文件        FileUtils.deleteDirectory(parentFileDir);        return Result.successMessage(200,"合并成功");      }else{        return Result.failMessage(400,"沒找到目錄");      }    } catch (Exception e) {      return Result.failMessage(400,e.getMessage());    }  }}

說明:

注解 @CrossOrigin 解決跨域問題

(3)Result

package com.example.demo.core;import com.alibaba.fastjson.JSON;/** * Created by Beibei on 19/02/22 * API響應(yīng)結(jié)果 */public class Result<T> {  private int code;  private String message;  private T data;  public Result setCode(Integer code) {    this.code = code;    return this;  }  public int getCode() {    return code;  }  public String getMessage() {    return message;  }  public Result setMessage(String message) {    this.message = message;    return this;  }  public T getData() {    return data;  }  public Result setData(T data) {    this.data = data;    return this;  }  @Override  public String toString() {    return JSON.toJSONString(this);  }  public static <T> Result<T> fail(Integer code,T data) {    Result<T> ret = new Result<T>();    ret.setCode(code);    ret.setData(data);    return ret;  }  public static <T> Result<T> failMessage(Integer code,String msg) {    Result<T> ret = new Result<T>();    ret.setCode(code);    ret.setMessage(msg);    return ret;  }  public static <T> Result<T> successMessage(Integer code,String msg) {    Result<T> ret = new Result<T>();    ret.setCode(code);    ret.setMessage(msg);    return ret;  }  public static <T> Result<T> success(Integer code,T data) {    Result<T> ret = new Result<T>();    ret.setCode(code);    ret.setData(data);    return ret;  }}

2.前端

(1)使用插件

webuploader,下載https://github.com/fex-team/webuploader/releases

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <link href="css/webuploader.css" rel="external nofollow" rel="stylesheet" type="text/css" />  <script type="text/javascript" src="jquery-1.10.1.min.js"></script>  <script type="text/javascript" src="dist/webuploader.min.js"></script></head><body>  <p id="uploader">   <p class="btns">     <p id="picker">選擇文件</p>     <button id="startBtn" class="btn btn-default">開始上傳</button>   </p>  </p></body><script type="text/javascript">var GUID = WebUploader.Base.guid();//一個(gè)GUIDvar uploader = WebUploader.create({  // swf文件路徑  swf: 'dist/Uploader.swf',  // 文件接收服務(wù)端。  server: 'http://localhost:8080/api/upload/part',  formData:{    guid : GUID  },  pick: '#picker',  chunked : true, // 分片處理  chunkSize : 1 * 1024 * 1024, // 每片1M,  chunkRetry : false,// 如果失敗,則不重試  threads : 1,// 上傳并發(fā)數(shù)。允許同時(shí)最大上傳進(jìn)程數(shù)。  resize: false});$("#startBtn").click(function () {  uploader.upload();});//當(dāng)文件上傳成功時(shí)觸發(fā)。uploader.on( "uploadSuccess", function( file ) {  $.post('http://localhost:8080/api/upload/merge', { guid: GUID, fileName: file.name}, function (data) {    if(data.code == 200){     alert('上傳成功!');    }   });});</script></html>

(2)不使用插件

直接用HTML5的File API

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">  <head>    <script src="jquery-1.10.1.min.js" type="text/javascript">    </script>    <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>  </head>  <body>    <p id="uploader">      <p class="btns">        <input id="file" name="file" type="file"/>        <br>          <br>            <button id="startBtn">              開始上傳            </button>          </br>        </br>      </p>      <p id="output">      </p>    </p>  </body>  <script type="text/javascript">    var status = 0;    var page = {    init: function(){      $("#startBtn").click($.proxy(this.upload, this));    },    upload: function(){      status = 0;      var GUID = this.guid();      var file = $("#file")[0].files[0], //文件對(duì)象        name = file.name,    //文件名        size = file.size;    //總大小      var shardSize = 20 * 1024 * 1024,  //以1MB為一個(gè)分片        shardCount = Math.ceil(size / shardSize); //總片數(shù)      for(var i = 0;i < shardCount;++i){        //計(jì)算每一片的起始與結(jié)束位置        var start = i * shardSize,        end = Math.min(size, start + shardSize);        var partFile = file.slice(start,end);        this.partUpload(GUID,partFile,name,shardCount,i);      }    },    partUpload:function(GUID,partFile,name,chunks,chunk){      //構(gòu)造一個(gè)表單,F(xiàn)ormData是HTML5新增的      var now = this;      var form = new FormData();      form.append("guid", GUID);      form.append("file", partFile); //slice方法用于切出文件的一部分      form.append("fileName", name);      form.append("chunks", chunks); //總片數(shù)      form.append("chunk", chunk);    //當(dāng)前是第幾片        //Ajax提交        $.ajax({          url: "http://localhost:8080/api/upload/part",          type: "POST",          data: form,          async: true,    //異步          processData: false, //很重要,告訴jquery不要對(duì)form進(jìn)行處理          contentType: false, //很重要,指定為false才能形成正確的Content-Type          success: function(data){            status++;            if(data.code == 200){              $("#output").html(status+ " / " + chunks);            }            if(status==chunks){              now.mergeFile(GUID,name);            }          }        });    },    mergeFile:function(GUID,name){      var formMerge = new FormData();      formMerge.append("guid", GUID);      formMerge.append("fileName", name);      $.ajax({        url: "http://localhost:8080/api/upload/merge",        type: "POST",        data: formMerge,        processData: false, //很重要,告訴jquery不要對(duì)form進(jìn)行處理        contentType: false, //很重要,指定為false才能形成正確的Content-Type        success: function(data){          if(data.code == 200){            alert('上傳成功!');          }        }      });    },    guid:function(prefix){        var counter = 0;        var guid = (+new Date()).toString( 32 ),          i = 0;        for ( ; i < 5; i++ ) {          guid += Math.floor( Math.random() * 65535 ).toString( 32 );        }        return (prefix || 'wu_') + guid + (counter++).toString( 32 );    }  };  $(function(){    page.init();  });  </script></html>

3.優(yōu)化 

springboot的默認(rèn)配置為10MB,前端分片改為20M時(shí),就會(huì)報(bào)錯(cuò)

org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (10486839) exceeds the configured maximum (10485760)

解決方法:

在 src/main/resources 下的 application.properties里添加

spring.servlet.multipart.max-file-size=30MBspring.servlet.multipart.max-request-size=35MB

說明:

設(shè)置的數(shù)值雖好比前端傳過來的大,要不容易報(bào)錯(cuò)

上述內(nèi)容就是springboot中怎么實(shí)現(xiàn)切割分片上傳功能,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(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