一、效果
点击“模板按钮”,就开始下载
二、根据磁盘路径下载
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--primary el-button--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代理。
后台代码与上相同。