一、效果

点击“模板按钮”,就开始下载

二、根据磁盘路径下载

1、前端代码

1、template

<el-button
        type="primary"
        icon="el-icon-download"
        @click="downloadTemplate('药材信息-模板.xlsx')">下载模板</el-button>

2、在main.js中注册原型方法

Vue.prototype.downloadTemplate = function(templateName) {
  const fileEntity = {
    fileName: templateName, // 文件名称
    filePath: globalProperties.importTemplate + templateName, // 文件路径
    downloadChannel: 'DIR' // 下载类型
  }
  baseAPI.ptsFileDownload(fileEntity).then(response => {
    fileDownload(response, templateName)
  }).catch(error => {
    console.log(error)
  })
}

注意:fileDownload中第一个参数是reponse还是response.data,要看拦截器中返回的是啥。如果返回的是resonse,则fileDownload中第一个参数为response.data。

如果response拦截器中返回的是response.data,那么fileDownload方法的第一个参数为response,而不是response.data。

// response 拦截器
service.interceptors.response.use(res => {
    const headers = res.headers
    // 此类为下载流文件,不拦截
    if (headers['content-type'] === 'application/octet-stream') {
        return res
    }
    if (headers['content-type'] === 'arrayBuffer;charset=UTF-8') {
        return res
    }
    var data = res.data;
    return data;
},err =>{
    console.log(err)
});

当content-type为application/octet-stream时,放行。

3、在global.js中指定Excel模板的位置

const globalProperties = {
  importTemplate: 'D:\\upload\\template\\'
}

在该位置添加Excel模板

在main.js中引入global.js

import './global.js'

4、安装并引入js-file-download

安装

npm install js-file-download

main.js中引入

import fileDownload from 'js-file-download'

5、baseAPI.js

import request from '@/utils/request'

export const baseAPI = {

  ptsFileDownload(query) {
    return request({
      url: '/fileServer/fileDownload',
      method: 'post',
      data: query,
      responseType: 'arraybuffer'
    })
  },
}

在main.js中引入baseAPI.js

import { baseAPI } from '@/api/base/baseAPI'

2、后台代码

下载的流程是:先读文件,在创建一个临时文件,再下载。

controller

@Controller
@RequestMapping("api/fileServer")
public class PtsFileController {    
    @Autowired
    private PtsFileService ptsFileService;

    @RequestMapping(value = "/fileDownload", method = { RequestMethod.POST, RequestMethod.GET })
    public String fileDownload(@RequestBody(required=true)PtsFileEntity fileEntity, HttpServletResponse response) throws Exception {
        ptsFileService.fileDownload(fileEntity, response);
        return null;
    }
}

service

@Service
public class PtsFileService {
    private static Logger logger = LoggerFactory.getLogger(PtsFileService.class);
    private static final String CHANNEL_DIR = "DIR"; // 根据文件的磁盘位置下载,如/usr/template/
    private static final String CHANNEL_URL = "URL"; // 根据URL下载,如http://...

    /**
     * 文件下载service入口
     *
     * @param fileEntity
     * @param response
     * @throws Exception
     */
    public void fileDownload(PtsFileEntity fileEntity, HttpServletResponse response) throws Exception {
        String fileName = fileEntity.getFileName();
        if (null == fileName || ("").equals(fileName.trim())) {
            logger.error("No FileName Found.");
            throw new Exception("No FileName Found.");
        }
        String downloadChannel = fileEntity.getDownloadChannel();
        if (null == downloadChannel || ("").equals(downloadChannel.trim())) {
            logger.error("Need to identity download channel.");
            throw new Exception("Need to identity download channel.");
        }
        if (CHANNEL_DIR.equalsIgnoreCase(downloadChannel)) {
            this.downloadFromDir(fileEntity, response);
        } else if (CHANNEL_URL.equalsIgnoreCase(downloadChannel)) {
            this.downloadFromUrl(fileEntity, response);
        }
    }

    /**
     * 从URL地址下载
     *
     * @param fileEntity
     * @param response
     * @throws Exception
     */
    private void downloadFromUrl(PtsFileEntity fileEntity, HttpServletResponse response) throws Exception {
        String filePath = fileEntity.getFilePath();
        String serverFilePath = fileEntity.getServerFilePath();
        if ((null == filePath || ("").equals(filePath.trim())) && (null == serverFilePath || ("").equals(serverFilePath.trim()))) {
            logger.error("No FilePath Found.");
            throw new Exception("No FilePath Found.");
        }
        String realFilePath = (null == filePath || ("").equals(filePath.trim())) ? serverFilePath : filePath;
        logger.info("Begin download file from Url: " + realFilePath);

        try {
            URL url = new URL(realFilePath);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //设置超时间为3秒
            conn.setConnectTimeout(3 * 1000);
            //防止屏蔽程序抓取而返回403错误
            conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

            //得到输入流
            InputStream inputStream = conn.getInputStream();
            //获取自己数组
            byte[] srcBytes = readInputStream(inputStream);
            this.createTempFile(fileEntity, srcBytes);
            this.download(fileEntity, response);
        } catch (IOException e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }

    /**
     * 从服务器路径下载
     *
     * @param fileEntity
     * @param response
     * @throws Exception
     */
    public void downloadFromDir(PtsFileEntity fileEntity, HttpServletResponse response) throws Exception {
        String filePath = fileEntity.getFilePath();
        String serverFilePath = fileEntity.getServerFilePath();
        if ((null == filePath || ("").equals(filePath.trim())) && (null == serverFilePath || ("").equals(serverFilePath.trim()))) {
            logger.error("No FilePath Found.");
            throw new Exception("No FilePath Found.");
        }
        String realFilePath = (null == filePath || ("").equals(filePath.trim())) ? serverFilePath : filePath;
        logger.info("Begin download file from Directory: " + realFilePath);

        try {
            // 以流的形式下载文件。
            InputStream fis;
            fis = new BufferedInputStream(new FileInputStream(realFilePath));
            byte[] srcBytes = new byte[fis.available()]; // 读取文件到字节数组中
            fis.read(srcBytes);
            fis.close();

            this.createTempFile(fileEntity, srcBytes); // 将字节数组中的内容存入临时文件中
            this.download(fileEntity, response); // 将临时文件内容读到字节数组中,再将字节数组中的内容写入输出流
        } catch (IOException ex) {
            ex.printStackTrace();
            throw new Exception(ex);
        }
    }

    /**
     * 创建临时文件
     *
     * @param fileEntity
     * @param srcBytes
     */
    public void createTempFile(PtsFileEntity fileEntity, byte[] srcBytes) throws Exception {
        byte[] targetBytes = srcBytes;

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String rootPath = this.getClass().getResource("/").getPath(); // /D:/project/prism/myProject/springboot-zwh/target/classes/
        String path1 = rootPath + "/download_files/";
        File exportPath1 = new File(path1);
        if (!exportPath1.exists()) exportPath1.mkdir();
        String path2 = path1 + simpleDateFormat.format(new Date());
        File exportPath2 = new File(path2);
        if (!exportPath2.exists()) exportPath2.mkdir();
        String fileDirPath = path2 + "/" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()).toString() + fileEntity.getFileName();
        File file = new File(fileDirPath);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(targetBytes);
            if (fos != null) {
                fos.close();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
            throw new Exception(ex);
        }

        fileEntity.setRealFilePath(fileDirPath);
    }

    /**
     * 拼接response header 并已流的形式下载文件
     *
     * @param fileEntity
     * @param response
     * @throws Exception
     */
    public void download(PtsFileEntity fileEntity, HttpServletResponse response) throws Exception {
        String fileName = fileEntity.getFileName();
        String realFilePath = fileEntity.getRealFilePath(); // 将临时路径中的文件
        File file = new File(realFilePath);

        try {
            // 以流的形式下载文件。
            InputStream fis;
            fis = new BufferedInputStream(new FileInputStream(realFilePath));
            byte[] srcBytes = new byte[fis.available()];
            fis.read(srcBytes);
            fis.close();

            // 清空response
            response.reset();
            // 设置response的Header
            response.setHeader("Content-type", "text/html;charset=UTF-8");
            response.setCharacterEncoding("utf-8");//设置编码集,文件名不会发生中文乱码

            response.setContentType("application/force-download");//
            response.setHeader("content-type", "application/octet-stream");
            response.addHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes(), "utf-8"));// 设置文件名
            response.addHeader("Content-Length", "" + file.length());
            response.setHeader("Access-Control-Allow-Origin", "*");
            OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
            toClient.write(srcBytes);
            toClient.flush();
            toClient.close();
        } catch (IOException ex) {
            throw new Exception(ex);
        }
    }

    /**
     * 从输入流中获取字节数组
     *
     * @param inputStream
     * @return
     * @throws IOException
     */
    public static byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }
}

临时文件的存储位置:

三、根据URL下载

1、前端代码

template

<el-button class="el-button el-button&#45;&#45;primary el-button&#45;&#45;small" type="primary" @click="downLoadTest">下载测试
        </el-button>

引入baseAPI

import { baseAPI } from '@/api/base/baseAPI'

script

downLoadTest() {
      const fileName = '阿里巴巴Java开发手册.pdf'
      const filePath = 'http://localhost:8888/template/阿里巴巴Java开发手册.pdf'//
      const fileEntity = {
        fileName: fileName,
        filePath: filePath,
        downloadChannel: 'URL' // DIR
      }
      baseAPI.ptsFileDownload(fileEntity).then(response => {
        console.info(response)
        fileDownload(response, fileName)
      }).catch(error => {
        console.log(error)
      })
    }

注意:fileDownload的第一个参数为response,因为响应拦截器中已经取出了data,如下所示

// response 拦截器
service.interceptors.response.use(res => {
    const headers = res.headers
    // 此类为下载流文件,不拦截
    if (headers['content-type'] === 'application/octet-stream') {
        return res
    }
    if (headers['content-type'] === 'arrayBuffer;charset=UTF-8') {
        return res
    }
    var data = res.data;
    return data;
},err =>{
    console.log(err)
});

在本地测试时,使用nginx代理。

后台代码与上相同。

 

posted on 2022-04-01 12:40  周文豪  阅读(3397)  评论(0编辑  收藏  举报