1. 導入jar依賴包:
ant-1.9.7.jar
commons-net-3.3.jar
提取碼:qy12
2. 建立一個工具類DownloadFileUtil.java,寫入以下代碼:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.net.ftp.FTPConnectionClosedException;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
public class DownloadFileUtil {
public static void createAndDownloadZip(String zipFileName, String filePath,
HttpServletRequest request, HttpServletResponse response)
throws FTPConnectionClosedException, IOException, Exception {
List fileList = Arrays.asList(filePath.split(";"));
ZipOutputStream zos = null;
String savePath = "C:/test";// 壓縮包存儲路徑
File dirFile = new File(savePath);
if(!dirFile.exists()) {
dirFile.mkdirs();
}
String zipPath = savePath + "/" + zipFileName + ".zip";
zos = new ZipOutputStream(new FileOutputStream(zipPath));// 建立壓縮包
zos.setEncoding("UTF-8");
toZip(fileList, zos);// 壓縮檔案
// 關閉
if(zos != null) {
try {
zos.flush();
zos.close();
} catch(IOException e) {
e.printStackTrace();
}
}
downloadZip(zipPath, request, response);// 下載下傳壓縮包
// 删除壓縮包(省略)
......
}
public static void toZip(List fileList, ZipOutputStream zos) throws IOException {
if(fileList != null && fileList.size() > 0) {
for(String file : fileList) {
// 檔案讀取到檔案流中
URL url = new URL("file:///" +file);// 加上file://辨別符表示本地的檔案
URLConnection connection = url.openConnection();
InputStream in = connection.getInputStream();
// 壓縮檔案名稱
// File.separatorChar = \
String[] fileStr = file.split("C:/test/");
String zipPath = fileStr[1].substring(0, fileStr[1].lastIndexOf("/") + 1);
zos.putNextEntry(new ZipEntry(zipPath));// 在壓縮檔案中建立檔案夾
zipPath += file.substring(file.lastIndexOf("/") + 1, file.length());
zos.putNextEntry(new ZipEntry(zipPath));// 在壓縮檔案對應位置建立檔案
// 把流中檔案寫到壓縮包中
byte[] buffer = new byte[1024];
int r = 0;
while((r = in.read(buffer)) != -1) {
zos.write(buffer, 0, r);
}
in.close();
// 删除(省略)
......
}
}
}
public static void downloadZip(String zipPath,
HttpServletRequest request, HttpServletResponse response)
throws FTPConnectionClosedException, IOException {
// 擷取要下載下傳的檔案名
String fileName = zipPath.substring(zipPath.lastIndexOf("/")+1, zipPath.length());
// System.out.println("檔案名:" +fileName);
response.reset();
response.setCharacterEncoding("utf-8");
response.setContentType("application/octet-stream");
// 設定content-disposition響應頭控制浏覽器以下載下傳的形式打開檔案
response.addHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(), "utf-8"));
// 擷取檔案輸入流
InputStream in = new FileInputStream(zipPath);
int len = 0;
byte[] buffer = new byte[1024];
OutputStream out = response.getOutputStream();
while((len = in.read(buffer)) > 0) {
//将緩沖區的資料輸出到用戶端浏覽器
out.write(buffer, 0, len);
}
in.close();
}
}
3. controller層次(項目是springMVC模式)寫一個方法:
@Controller
@RequestMapping("test")
@ResponseBody
@RequestMapping("/downloadFile")
public void downloadFile(String ids,
HttpServletRequest request, HttpServletResponse response)
throws FTPConnectionClosedException, IOException, Exception {
......
// 壓縮下載下傳檔案
DownloadFileUtil.createAndDownloadZip(zipName, filePath, request, response);
}
注意:(1)zipName:zip壓縮包名字;
(2)filePath:要壓縮檔案的位址拼接(拼接符号是英文的分号;)[多個檔案路徑才需要拼接];
4. 前端js:
window.location.href = path + "test/downloadFile?ids="+ids;
注意:
(1)如果要用ajax發送請求的話,浏覽器會沒有任何反應;
原因:
1)因為ajax的傳回值類型是json,text,html,xml類型,或者可以說ajax的接收類型隻能是string字元串,不是流類型,是以無法實作檔案下載下傳。
2)但用ajax仍然可以獲得檔案的内容,該檔案将被保留在記憶體中,無法将檔案儲存到磁盤。這是因為JavaScript無法和磁盤進行互動,否則這會是一個嚴重的安全問題,js無法調用到浏覽器的下載下傳處理機制和程式,否則會被浏覽器阻塞。
(2)如果想要頁面對浏覽器下載下傳進行監聽進行一些操作的話,我們需要使用fetch()方法;
var url = path + "test/downloadFile?ids="+ids;
fetch(url).then(res => res.blob()).then(data => {
// 操作
......
var blobUrl = window.URL.createObjectURL(data);
downloads(blobUrl, fileName);
});
function downloads(blobUrl, fileName) {
const a = document.createElement('a');
a.style.display = 'none';
a.download = fileName + '.zip';
a.href = blobUrl;
a.click();
document.body.removeChild(a);
}
當然,萬惡的IE浏覽器不支援fetch()方法,需要改為XMLHttpRequest來做,上述前端js代碼改為:
var height = ($(document).height() - 180)/2 + "px";
var height2 = ($(document).height() - 48)/2 + "px";
$(function(){
$(document).off('click', '.downloadAll');
$(document).on('click', '.downloadAll', function(){
var $table = $(this).parent().parent().parent().parent();
var row = 0;
var ids = '';
$table.find('tbody').children().each(function(){
var $tds = $(this).children();
var $checkbox = $tds.eq(0).find('input[type=checkbox]');
if($checkbox.prop("checked")) {
ids += $tds.eq(2).html() + ";";
row++;
}
});
if(row > 0) {
ids = ids.slice(0, ids.length-1);
layer.confirm('确認導出選中的 ' + row +' 條資料?', {
title: '提示',
offset: height,
btn: ['确定', '取消']
}, function(){
layer.closeAll();
var url = path + "test/downloadFile?ids="+ids;
downloadPDF(url);
});
} else {
layer.msg('請選中要導出檔案的資料', {
offset: height
});
}
return false;
});
});
function downloadPDF(url) {
var xhr;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏覽器執行代碼
xhr = new XMLHttpRequest();
} else {
// IE6, IE5 浏覽器執行代碼
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
if (xhr != null) {
xhr.open("GET", url, true);
xhr.responseType = "blob";
xhr.onload = function () {// 請求完成
if (this.status == 200) {
var blob = this.response;
var blobUrl = window.URL.createObjectURL(blob);// 建立下載下傳的連結
var fileName = createFileName();
// 判斷是否是IE浏覽器,是的話傳回true
// 判斷的原因是downloads()方法需要用到download屬性,IE浏覽器不支援
if (window.navigator.msSaveBlob) {
try {
window.navigator.msSaveBlob(blob, fileName+'.zip');
} catch (e) {
console.log(e);
}
} else {
downloads(blobUrl, fileName);
}
}
}
xhr.send();
} else {
layer.msg('你的浏覽器不支援XMLHTTP,請換一個浏覽器試試!', {
offset: height
});
}
}
function createFileName() {
var myDate = new Date();
var curr_date = myDate.getDate();
var curr_month = myDate.getMonth() + 1;
var curr_year = myDate.getFullYear();
String(curr_month).length < 2 ? (curr_month = "0" + curr_month): curr_month;
String(curr_date).length < 2 ? (curr_date = "0" + curr_date): curr_date;
var curr_hour = myDate.getHours();
var curr_minute = myDate.getMinutes();
var curr_second = myDate.getSeconds();
String(curr_hour).length < 2 ? (curr_hour = "0" + curr_hour): curr_hour;
String(curr_minute).length < 2 ? (curr_minute = "0" + curr_minute): curr_minute;
String(curr_second).length < 2 ? (curr_second = "0" + curr_second): curr_second;
var curr_time = curr_year + curr_month + curr_date + curr_hour + curr_minute + curr_second;
return curr_time;
}
function downloads(blobUrl, name) {
var a = document.createElement('a');
a.style.display = 'none';
a.download = name + '.zip';
a.href = blobUrl;
a.click();
// document.body.removeChild(a);// 移除
}
其中在賦予下載下傳連結時,我一開始是通過建立标簽,賦予下載下傳連結,然後觸發點選事件的方法來建立一個本地的壓縮包來接受二進制資料流,但萬惡的IE浏覽器又不起作用,因為那個方法需要用到标簽的download屬性,而這恰恰IE浏覽器都不支援,是以我們隻能改為調用IE浏覽器自帶的儲存按鈕,代碼如下:
// 判斷是否是IE浏覽器,是的話傳回true
// 判斷的原因是downloads()方法需要用到download屬性,IE浏覽器不支援
if (window.navigator.msSaveBlob) {
try {
window.navigator.msSaveBlob(blob, fileName+'.zip');
} catch (e) {
console.log(e);
}
} else {
downloads(blobUrl, fileName);
}
(3)至于下載下傳檔案前詢問下載下傳位址這些,簡單點可以通過設定浏覽器下載下傳時都詢問下載下傳位址來實作,複雜點就自己實作一個下載下傳詢問選擇框。
傳送門: