文件下载
文件下载
碰到文件下载通常是这两种业务情况:
Web端下载文件
Web端查询列表导出EXCEL
输出流
文件下载都是通过 页面响应的 输出流 来实现的
不同编程语言输出流的名字可能不同。
在 Java 的 Spring Boot 项目中,在控制器上可以通过 HttpServletResponse 来获取输出流 ServletOutputStream。
ServletOutputStream outputStream = response.getOutputStream();
输出流输出到页面的是 byte数组
页面输出字符串
Java 中字符串可以通过 getBytes() 方法转化成对应的 byte 数组byte[] byteArr = ("标题,内容\n").getBytes();
因此,我们可以通过 ServletOutputStream 来输出字符串
@GetMapping("output/string")
private void outputStr(HttpServletResponse response){
try {
ServletOutputStream outputStream = response.getOutputStream();
byte[] data = "abc".getBytes();
outputStream.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
效果如下:
页面输出文件
页面输出文件,就是将文件以 byte 数组的形式输出,这里我输出的是 TestClass.java 文件
/*一次性将文件所有字节读取出来,并输出出去*/
@GetMapping("file/outputfile")
public void outputfile(HttpServletRequest request, HttpServletResponse response) {
try {
ServletOutputStream outputStream = response.getOutputStream();
File file = new File("/Users/adminqian/shen/TestClass.java");
FileInputStream inputStream = new FileInputStream(file);
/*一次性读取文件的所有字节内容*/
int length = inputStream.available();
byte[] data = new byte[length];
inputStream.read(data);
outputStream.write(data);
/*如果文件过大,会直接提示 java.lang.OutOfMemoryError: Requested array size exceeds VM limit */
outputStream.flush();
outputStream.close();
} catch (IOException e) {
log(e.toString());
}
}
展示的效果是:
如果文件是一个压缩包呢,效果就是下面的样子,由于压缩包不是文本文件,所以输出的字节数组就成了乱码。
页面下载文件
输出文件内容并不是我们的业务需求,我们要下载文件,又该如何处理呢?
我们只需设置 HTTP 响应的响应头即可。
String name = "12.iso";
response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
response.setHeader("Content-Disposition", "attachment; filename=" + name);
通过设置响应头的 Content-Type 和 Content-Disposition 即可。
Content-Type 设置为 application/octet-stream;charset=utf-8 即内容输出的是二进制。
Content-Disposition 设置为 attachment 即输出的是文件,文件名字为后面的 filename。
问题:输出的文件名乱码
原因是在不同浏览器下,编码不同导致的。可以判断浏览器类型,来使用不同的编码。
if (request.getHeader("User-Agent") != null) {
String agent = request.getHeader("User-Agent").toLowerCase();
//识别IE浏览器
if (agent != null && (agent.indexOf("msie") != -1 ||
(agent.indexOf("rv") != -1 && agent.indexOf("firefox") == -1))) {
name = URLEncoder.encode(name, "UTF-8");
} else {
name = new String(name.getBytes("UTF-8"), "ISO-8859-1");
}
}
输出流分块输出
不管是输出字符串,还是文件,我们都是一次性输出了数据。
即调用了一次 outputStream.write(data);
方法。
如果 data 特别大,而可能会出现问题。所以建议是通过循环的形式,一块一块的输出。
比如:
@GetMapping("file/output")
public void getFile(HttpServletRequest request, HttpServletResponse response) {
try {
String name = "12.csv";
response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
response.setHeader("Content-Disposition", "attachment; filename=" + name);
ServletOutputStream outputStream = response.getOutputStream();
byte[] byteArr = ("标题,内容\n").getBytes();
outputStream.write(byteArr);
for (int i = 0; i < 10000 * 100; i++) {
outputStream.write(String.format("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8, i + 9).getBytes());
}
outputStream.flush();
outputStream.close();
} catch (IOException e) {
log(e.toString());
}
}
/*将文件一部分一部分的读取,然后一部分一部分的输出到浏览器*/
@GetMapping("file/output2")
public void getFile2(HttpServletRequest request, HttpServletResponse response) {
try {
String name = "12.iso";
response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
response.setHeader("Content-Disposition", "attachment; filename=" + name);
ServletOutputStream outputStream = response.getOutputStream();
File file = new File("/Users/adminqian/shen/cn_windows_7_enterprise_x64_dvd_x15-70741.iso");
FileInputStream inputStream = new FileInputStream(file);
byte[] chunk = new byte[1024];
int result;
do {
result = inputStream.read(chunk);
outputStream.write(chunk);
}
while (result != -1);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
log(e.toString());
}
}
大文件下载的建议
首先,大文件输出肯定要采用分块输出的形式。
另外,不管是直接将字符串输出,还是读取已有文件,都可以实现文件的下载。
因此我们可以根据实际情况来采用不同的方法。
比如,我们把数据库中的数据导出来这样的业务场景,我们就可以直接拼成以逗号分隔的字符串。
然后输出出来。
注意不要先将数据保存到磁盘文件上,然后将文件再输出出来。这样肯定会影响效率。
有时候,从数据库中查找出来的记录,我们并不完全放在文件中,也不要再遍历数据,
生成新的集合数据,直接遍历输出的时候,有选择性的输出即可。
现有类库
HttpHelper 查看
下载接口文件/下载网络文件
//浏览器下载文件
/* 将第三方图片在自己页面上展示,(url地址如果能直接用更好)*/
/* 将接口获取的文件输出到浏览器 */
@GetMapping("file")
public void httpDownloadFile(HttpServletResponse response) throws IOException {
String url = "http://oimagec6.ydstatic.com/image?id=7347104849285270631&product=dict-homepage&w=&h=&fill=0&cw=&ch=&sbc=0&cgra=CENTER&of=jpeg";
//response.setContentLengthLong(contentLength);
response.setHeader("Content-Type","image/jpeg");
ServletOutputStream outputStream = response.getOutputStream();
HttpHelper.downloadFile(url, null, outputStream);
}
// 浏览器下载文件,从服务器下载文件
@GetMapping("/file/get")
public void get(String filePath, String displayName, String type, HttpServletRequest request, HttpServletResponse response) throws IOException {
File image = new File(filePath);
FileInputStream inputStream = new FileInputStream(image);
int length = inputStream.available();
byte data[] = new byte[length];
inputStream.read(data);
String name = image.getName();
if (StringUtil.isNotBlank(displayName))
name = displayName;
if (request.getHeader("User-Agent") != null) {
String agent = request.getHeader("User-Agent").toLowerCase();
//识别IE浏览器
if (agent != null && (agent.indexOf("msie") != -1 ||
(agent.indexOf("rv") != -1 && agent.indexOf("firefox") == -1))) {
name = URLEncoder.encode(name, "UTF-8");
} else if (agent != null && agent.indexOf("firefox") != -1) {
name = new String(name.getBytes("UTF-8"), "ISO-8859-1");
} else {
//name = new String(name.getBytes("UTF-8"), "ISO-8859-1");
name = URLEncoder.encode(name, "UTF-8");
}
}
response.setContentLength(length);
if(type != null && type.equalsIgnoreCase("html")){
response.setHeader("Content-Type", "text/html;");
}else{
response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
response.setHeader("Content-Disposition", "attachment; filename=" + name);
}
OutputStream toClient = response.getOutputStream();
toClient.write(data);
toClient.flush();
}
//保存文件到本地
public static void saveFileToLocal() throws IOException {
String url = "http://oimagec6.ydstatic.com/image?id=7347104849285270631&product=dict-homepage&w=&h=&fill=0&cw=&ch=&sbc=0&cgra=CENTER&of=jpeg";
HttpHelper.downloadFile(url, null, "e:/abc.png");
}
*昵称:
*邮箱:
个人站点:
*想说的话: