• 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;
    }
  • 相关阅读:
    Git使用笔记
    javascript获取表单值的7种方式
    javascript里阻止事件冒泡
    PHP面向对象04_串行化
    MySQL数据库锁定机制
    SAP R3和JAVA交换数据之JCO
    @XStreamAlias使用
    JCO 自定义DestinationDataProvider
    IBM websphere MQ 消息发送与获取
    WebSphere MQ 入门指南
  • 原文地址:https://www.cnblogs.com/1204it-ly/p/13877407.html
Copyright © 2020-2023  润新知