SpringBoot Vue 批量上傳附件與打包壓縮下載附件


前言:

在傳統的管理系統項目一般都遇到有附件的上傳與下載業務,我最近參與的項目中用到了附件的上傳與下載功能,今天正好有空整理一下。

業務邏輯:

附件先上傳到臨時目錄,業務頁面點擊保存后臨時目錄附件移動到正式目錄下,並將業務數據和附件相關信息保存入庫。

廢話不多說,直接上代碼!!!

  1. 批量上傳附件

前端 vue 代碼

前端 使用 Element-UI 的上傳控件

<el-card>
            <h1>1.上傳附件</h1>
            <el-upload
                    class="upload-demo"
                    drag
                    :headers="headers"
                    name="files"
                    :on-remove="remove"
                    :on-success="success"
                    :auto-upload="true"
                    action="api/basic/file/uploadFile"
                    multiple>
                <i class="el-icon-upload"></i>
                <div class="el-upload__text">將文件拖到此處,或<em>點擊上傳</em></div>
                <div class="el-upload__tip" slot="tip">只能上傳doc docx文件,且不超10MB</div>
            </el-upload>
</el-card>

上圖中   :on-success="success"  代表附件上傳成功后的鈎子

接收后端返回的臨時目錄

  //文件上傳成功時的鈎子
  success(res, file, fileList) {
    this.fileList.length = 0
    for (let i = 0; i < fileList.length; i++) {
      if(fileList[i].response)
        this.fileList.push(fileList[i].response.list[0])
    }
    console.log(`已上傳文件列表`, this.fileList)
  },

后端 接口

配置文件的上傳路徑(前綴) 在application-dev.yml 中 ,項目打包發布到對應的服務,附件上傳后的位置就是配置的路徑

獲取application-dev.yml配置路徑

/**
 *@Description 文件上傳處理方法 臨時目錄
 *@Param MultipartFile file
 *@Author
 *@Date  2020/4/22  18:24
 *@return R
 */
@PostMapping("/uploadFile")
public R uploadFile(@RequestParam(required = false, value = "files") MultipartFile[] multipartFiles) throws IOException {
    List<AccessoryQueryExt> list = new ArrayList<AccessoryQueryExt>();
    if(multipartFiles.length > 0){
        for (MultipartFile file : multipartFiles){
            R r = fileService.uploadFile(file,filePath);
            String path = (String) r.get("path");
            String fileName = (String) r.get("fileName");
            AccessoryQueryExt accessoryQueryExt = new AccessoryQueryExt();
            accessoryQueryExt.setName(fileName);
            accessoryQueryExt.setTemporaryPath(path);
            list.add(accessoryQueryExt);
        }
        return R.success().put("list",list);
    }
    return R.fail();
}

上傳的實現類

@Transactional(rollbackFor = Exception.class)
@Override
public R uploadFile(MultipartFile file,String filePath) throws IOException{
    // 判斷文件是否為空
    if(file.isEmpty()){
        return R.fail().setMessage("file is empty");
    }
    String fileName = file.getOriginalFilename();
    // 獲取文件名后綴
    String suffixName = fileName.substring(fileName.lastIndexOf("."));
    // 附件重命名 = 生成32位隨機碼 + 源文件后綴
    String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
    String newFileName = uuid.concat(suffixName);
    // 設置文件臨時存儲路徑
    String path = "Temporary".concat("/").concat(newFileName);
    // 全路徑
    String fullPath = filePath.concat(path);
    File dest = new File(fullPath);
    // 判斷目錄是否存在
    if(!dest.getParentFile().exists()){
        dest.getParentFile().mkdirs();
    }
    // 寫入文件
    file.transferTo(dest);
    // 將臨時目錄和原附件名稱返回
    return R.success().put("path",path).put("fileName",fileName);
}

2.  移動附件至正式目錄

前端 vue

        <el-card>
            <h1>2.保存附件</h1>
            <el-button @click="save()">保存表單</el-button>
        </el-card>
    save(){
        console.log(this.fileList)
        for (let i = 0; i < this.fileList.length; i++){
          this.fileList[i].isDelete = false // 此處為業務邏輯參數
          this.fileList[i].relaId = "1" // 此處為業務邏輯參數
        }

        http
          .post('basic/file/saveFile', this.fileList)
          .then(function (res) {
           console.log(res)
          })
          .catch(function (err) {
            console.log(err);
          });
      }

后端接口及實現

 /**
 *@Description  保存附件至正式目錄
 *@Param  accessoryQueryExt 附件接收參數實體
 *@Author 
 *@Date  2020/5/25  11:50
 *@return  R
 */
@PostMapping("/saveFile")
public R saveFile(@RequestBody List<AccessoryQueryExt> accessoryQueryExts){
    for (AccessoryQueryExt accessoryQueryExt : accessoryQueryExts){
        fileService.saveFile(accessoryQueryExt, filePath);
    }
    return R.success();
}
@Override
public R saveFile(AccessoryQueryExt accessoryQueryExt, String filePath) {
    if(accessoryQueryExt.getIsDelete()){
        // 刪除附件表中對應的數據並刪除附件
        QueryWrapper<Accessory> wrapper = new QueryWrapper<Accessory>();
        wrapper.eq("RELA_ID", accessoryQueryExt.getRelaId());
        List<Accessory> accessories = this.baseMapper.selectList(wrapper);
        if(accessories != null && accessories.size() > 0){
            for (Accessory accessory : accessories){
                // 刪除附件
                new File(filePath.concat(accessory.getPath())).delete();
                this.baseMapper.deleteById(accessory.getAccessoryId());
            }
        }
    }
    // 獲取附件臨時目錄
    String temporaryPath = accessoryQueryExt.getTemporaryPath();
    if(StringUtils.isEmpty(temporaryPath)){
        throw new RuntimeException("附件臨時目錄為空");
    }

    String formatDate = new SimpleDateFormat("yyyyMM").format(new Date());
    // 附件正式目錄 /data/uploadFile/業務類型碼/yyyymm  業務類型碼 從字典表中獲取
    String businessTypeName = "common";
    if(StringUtils.isNotBlank(accessoryQueryExt.getBusinessTypeName())){
        businessTypeName = accessoryQueryExt.getBusinessTypeName();
    }
    String savePath =  businessTypeName.concat("/").concat(formatDate).concat("/").concat(temporaryPath.substring(10));
    String formalPath = filePath.concat(savePath);
    File dest = new File(formalPath);
    // 判斷目錄是否存在
    if(!dest.getParentFile().exists()){
        dest.getParentFile().mkdirs();
    }
    File fileOld = new File(filePath.concat(temporaryPath));
    String sizeUnit = getPrintSize(fileOld.length());
    String[] split = sizeUnit.split(":");
    if(fileOld.renameTo(dest)){
        // 將文件上傳信息寫入數據庫
        Accessory accessory = new Accessory();
        // 關聯ID
        accessory.setRelaId(new BigDecimal(accessoryQueryExt.getRelaId()));
        String perfix = accessoryQueryExt.getName().substring(accessoryQueryExt.getName().lastIndexOf(".") + 1);
        if(FileTypeUtil.fileType(perfix)){
            // 文件類型  01 文檔 02 圖片 03 視頻
            accessory.setFileType("01");
        } else if(FileTypeUtil.imageType(perfix)){
            accessory.setFileType("02");
        } else if(FileTypeUtil.videoType(perfix)){
            accessory.setFileType("03");
        }

        if(filePath.indexOf(":") != -1){
            //文件存儲類型 01 本地 02 遠程
            accessory.setFileStoreType("01");
        } else {
            accessory.setFileStoreType("02");
        }
        //文件名
        accessory.setName(accessoryQueryExt.getName());
        accessory.setPath(savePath);
        // 附件大小
        accessory.setSize(new BigDecimal(split[0]));
        // 附件單位
        accessory.setUnit(split[1]);
        this.baseMapper.insert(accessory);
        return R.success();
    }
    return R.fail().setMessage("移動附件失敗");
}


/**
 * 根據文件大小轉換為B、KB、MB、GB單位字符串顯示
 * @param fileSize 文件的大小(long型)
 * @return 返回 轉換后帶有單位的字符串
 */
public static String getPrintSize(long fileSize){
    String strFileSize = null;
    if(fileSize < 1024){
        strFileSize = fileSize + ":" +"B";
        return strFileSize;
    }
    DecimalFormat df = new DecimalFormat("######0.00");
    if ((fileSize >= 1024) && (fileSize < 1024*1024)){ //KB
        strFileSize = df.format(((double)fileSize)/1024) + ":" +"KB";
    }else if((fileSize >= 1024*1024)&&(fileSize < 1024*1024*1024)){ //MB
        strFileSize = df.format(((double)fileSize)/(1024*1024)) + ":" +"MB";
    }else{ //GB
        strFileSize = df.format(((double)fileSize)/(1024*1024*1024)) + ":" +"GB";
    }
    return strFileSize;
}
  1. 批量下載並壓縮

前端 vue

    <el-card>
        <h1>3.下載附件</h1>
        <el-button @click="download()">下載附件</el-button>
    </el-card>
download:function(){
  http
    ({
      method: 'post',
      url: 'basic/file/downloadFile?relaId=1',
      data: {}, //可選參數,后台用 @RequestBody接收
      responseType: 'blob'
    })
    .then(function(res)
    {
      let data= res.data
      var blob = new Blob([data])
      // 創建下載的鏈接
      var a = document.createElement('a');
      a.style.display = 'none';
      var href = window.URL.createObjectURL(blob);
      a.href = href;
      //顯示下載文件名 獲取后端返回的文件名
      let filename= res.headers['content-disposition'].split('=')[1]
      // 文件名中有中文 則對文件名進行轉碼 decodeURI
      a.download= decodeURI(filename)   
      // 利用a標簽做下載       
      document.body.appendChild(a);
      // 點擊下載
      a.click();
      // 下載后移除元素
      document.body.removeChild(a);
      // 釋放掉blob對象
      window.URL.revokeObjectURL(href);
    })
    .catch(function (error) 
    { 
      console.info('response.error'+error.message)
    });


}

后端 接口

/**
 *@Description  文件下載處理方法
 *@Param relaId 關聯ID
 *@Author 
 *@Date  2020/4/23  13:47
 *@return R
 */
@GetMapping("/downloadFile")
public void downloadFile(HttpServletResponse response,@RequestParam("relaId") String relaId){
    fileService.downloadFile(relaId,response,filePath);
}
@Override
public void downloadFile(String  relaId, HttpServletResponse response, String filePath)     
{
    // 從數據庫獲取文件信息
    QueryWrapper<Accessory> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("RELA_ID",relaId);
    List<Accessory> accessoryList = this.baseMapper.selectList(queryWrapper);
    if(accessoryList != null && accessoryList.size() > 1){
        // 壓縮包名稱
        String fileName = String.valueOf(System.currentTimeMillis());
        batchDownload(accessoryList,fileName, response, filePath);
    }
    // 若只有一個文件,則不用壓縮
    if(accessoryList != null && accessoryList.size() == 1){
        Accessory accessory = accessoryList.get(0);
        download(accessory,filePath,response);
    }

}


/**
 * 批量下載並壓縮
 */
public void batchDownload(List<Accessory> accessoryList,String filename, HttpServletResponse response, String filePath) {
    //創建zip文件並返回zip文件路徑
    String zipPath = createZipAndReturnPath(accessoryList, filename, filePath);
    try {
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/zip;charset=utf-8");
        response.setHeader("content-disposition", "attachment;filename="+filename+".zip");
        // 返回文件名,需要設置下面代碼
        response.setHeader("Access-Control-Expose-Headers", "content-disposition,content-length");
        //開始下載
        BufferedInputStream is = new BufferedInputStream(new FileInputStream(new File(zipPath)));
        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        byte[] buff = new byte[1024];
        int len = 0;
        while ((len = is.read(buff, 0, buff.length)) != -1) {
            out.write(buff, 0, len);
        }
        out.close();
        out.flush();
        is.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}


/**
 * 批量打包
 * @param filePath 根目錄
 * @param filename 壓縮包名稱
 * @return zip文件保存絕對路徑
 */
public String createZipAndReturnPath(List<Accessory> accessoryList,String filename, String filePath) {
    try {
        //生成打包下載后的zip文件:文件名.zip
        String papersZipName = filename+".zip";
        //zip文件保存路徑
        String zipPath = filePath + papersZipName;
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipPath));
        for (Accessory accessory : accessoryList){
            //獲得文件相對路徑
            String relativePath = accessory.getPath();
            //獲得文件名
            String fileName = accessory.getName();
            //獲得下載文件完整路徑
            String downloadPath = filePath + relativePath;
            FileInputStream fis = new FileInputStream(downloadPath);
            out.putNextEntry(new ZipEntry(fileName));
            //寫入壓縮包
            int len;
            byte[] buffer = new byte[1024];
            while ((len = fis.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
            out.closeEntry();
            fis.close();
        }
        out.close();
        out.flush();
        return zipPath;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}


/**
 * 下載
 */
public void download(Accessory accessory, String filePath, HttpServletResponse response){
    String fileName  = accessory.getName();
    String path = accessory.getPath();
    File file = new File(filePath.concat(path));
    FileInputStream fis = null;
    BufferedInputStream bis = null;
    try {
        String extension = ext(path);
        //設置response內容的類型
        response = setContextType(extension, response);
        // 設置文件名
        response.addHeader("content-disposition", "attachment;fileName=" + URLEncoder.encode(fileName,"UTF-8" ));
        response.setHeader("content-length", Long.toString(file.length()));
        // 返回文件名,需要設置下面代碼
        response.setHeader("Access-Control-Expose-Headers", "content-disposition,content-length");
        byte[] buffer = new byte[1024];
        fis = new FileInputStream(file);
        bis = new BufferedInputStream(fis);
        OutputStream os = response.getOutputStream();
        int i = bis.read(buffer);
        while (i != -1) {
            os.write(buffer, 0, i);
            i = bis.read(buffer);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (bis != null) {
            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


/**
 * 設置response內容的類型
 */
public static HttpServletResponse setContextType(String extension, HttpServletResponse response){
    if("JPG".equalsIgnoreCase(extension)){
        response.setContentType("application/x-jpg");
    } else if("GIF".equalsIgnoreCase(extension)){
        response.setContentType("image/gif");
    } else if("mp4".equalsIgnoreCase(extension)){
        response.setContentType("video/mpeg4");
    } else if("avi".equalsIgnoreCase(extension)){
        response.setContentType("video/avi");
    } else if("WMV".equalsIgnoreCase(extension)){
        response.setContentType("video/x-ms-wmv");
    } else if("txt".equalsIgnoreCase(extension)){
        response.setContentType("text/plain");
    } else if("doc".equalsIgnoreCase(extension)){
        response.setContentType("application/msword");
    } else if("pdf".equalsIgnoreCase(extension)){
        response.setContentType("application/pdf");
    } else if("xls".equalsIgnoreCase(extension)){
        response.setContentType("application/vnd.ms-excel");
    }
    return response;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM