1. 导入jar依赖包:
ant-1.9.7.jar
commons-net-3.3.jar
百度网盘:https://pan.baidu.com/s/1u7m5NYans-UK_h8VSEPfbA
提取码: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 {
/**
* 创建压缩包并下载
* @param zipFileName 压缩包名称
* @param filePath 要下载的文件具体路径拼接(;)
* @param request
* @param response
* @throws FTPConnectionClosedException
* @throws IOException
*/
public static void createAndDownloadZip(String zipFileName, String filePath,
HttpServletRequest request, HttpServletResponse response)
throws FTPConnectionClosedException, IOException, Exception {
List<String> 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);// 下载压缩包
// 删除压缩包(省略)
......
}
/**
* 压缩文件
* @param fileList 文件具体位置
* @param zos
* @throws IOException
*/
public static void toZip(List<String> 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();
// 删除(省略)
......
}
}
}
/**
* 下载zip压缩包到本地
* @param zipPath 压缩包绝对路径
* @param request
* @param response
* @throws FTPConnectionClosedException
* @throws IOException
*/
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);// 移除<a>
}
其中在赋予下载链接时,我一开始是通过创建<a>标签,赋予下载链接,然后触发点击事件的方法来创建一个本地的压缩包来接受二进制数据流,但万恶的IE浏览器又不起作用,因为那个方法需要用到<a>标签的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)至于下载文件前询问下载地址这些,简单点可以通过设置浏览器下载时都询问下载地址来实现,复杂点就自己实现一个下载询问选择框。
传送门:
Java 通过wkhtmltopdf在线生成PDF及遇到的坑:https://my.oschina.net/u/3986411/blog/3291128