一、效果
點擊“模板按鈕”,就開始下載
二、根據磁盤路徑下載
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代理。
后台代碼與上相同。