学习Java的文件上传/下载需要先明白一下几点:
1、下载的资源,有两种:
1)、本地文件:即项目运行时可访问的文件目录,比如,在本机Idea中运行一个 fileServer,那么 fileServer 可访问的你电脑目录下文件做为下载资源;或者 fileServer 在服务器上运行,就是服务器上可访问的目录下文件资源。
2)、远程文件:fileServer 运行的网络环境中 可访问的 Ftp/SFtp或其他服务接口中获取的文件字节码,I/O流等。
2、下载方式: 就是谁来获取这些“下载文件”,比如 :客户端的浏览器或这其他开发者通过接口获取你的文件。
总结:首先,以上两点,你需要知道, 第一,提供的接口中文件从哪里来,是本地文件还是远程文件 ;第二、文件提供给谁下载,你的返回类型是什么,是一个字节码、还是一个输出流。
一、提供“本地文件” ,给浏览器、前端Ajax或其他开发者接口来下载
1.1、controllert 类
package com.appms.controller;
import com.appms.util.Util;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RestController
@CrossOrigin("*")
@RequestMapping("file")
public class FileController {
@GetMapping("/download")
public void download(HttpServletRequest request, HttpServletResponse response) throws IOException {
Util.downloadFile(request, response);
}
}
1.2、实现类 :Util.class
public static void downloadFile(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// 本地文件地址,文件名称,我是在本机运行,如果是服务器的话,地址可能是 ../fileServer/file/deploy2.sh
String filePath = "/Users/jjshen/bysj/deploy2.sh";
String fileName = "deploy2.sh";
// 获取浏览器的信息
String agent = request.getHeader("USER-AGENT");
if (agent != null && agent.toLowerCase().indexOf(FIRE_FOX) > 0) {
//火狐浏览器自己会对URL进行一次URL转码所以区别处理
response.setHeader("Content-Disposition",
"attachment; filename=" + new String(fileName.getBytes("GB2312"), "ISO-8859-1"));
} else if (agent.toLowerCase().indexOf(SAFARI) > 0) {
//苹果浏览器需要用ISO 而且文件名得用UTF-8
response.setHeader("Content-Disposition",
"attachment; filename=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
} else {
//其他的浏览器
response.setHeader("Content-Disposition",
"attachment; filename=\"" + java.net.URLEncoder.encode(fileName, "UTF-8"));
}
File file = new File(filePath);
FileInputStream fileInputStream = new FileInputStream(file);
byte[] fileByte = new byte[(int) file.length()];
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len;
while ((len = fileInputStream.read(bytes, 0, bytes.length)) != -1) {
byteArrayOutputStream.write(bytes, 0, len);
}
byteArrayOutputStream.close();
fileByte = byteArrayOutputStream.toByteArray();
OutputStream outputStream = null;
outputStream = response.getOutputStream();
outputStream.write(fileByte);
outputStream.flush();
outputStream.close();
}
代码解释:文件下载是将文件流写入 HttpServletResponse中,在header中添加 浏览器的解析方式,和文件名:
Content-Disposition, attachment ; fileName = ? 这样如果在浏览器中直接访问接口,就可以直接下载了。如:浏览直接访问:http://127.0.0.1:9080/file/download,就可以在浏览器下载了。
1.3、前端页面调用
定义 responseType的类型为 “blob” ,然后通过 document 创建一个 a 标签,然后将文件名称、文件流设置进去即可。
// 初始化一个HTTP请求
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:9080/file/download');
xhr.responseType = 'blob';
xhr.send();
// 回调函数
xhr.onload = function () {
if (this.status === 200) { // HTTP 状态码
const blob = this.response;
const reader = new FileReader();
reader.readAsDataURL(blob); // 读取指定的 Blob 或 File 对象
// 处理读取事件
reader.onload = function (e) {
// 响应头存在中文需要编码
const headerName = decodeURIComponent(escape(xhr.getResponseHeader('Content-disposition')));
const headerNameArr = headerName.split('=');
const fileName = headerNameArr[headerNameArr.length - 1];
let aLink = document.createElement('a');
aLink.download = fileName;
aLink.href = e.target.result;
aLink.click();
$(aLink).remove();// 移除元素,但未清理DOM元素引用
aLink = null; // 解除引用,内存释放
alert('下载成功!');
};
} else {
tools.errorAlerts('下载失败!');
}
};
1.4、其他服务接口调用:
调用接口,然后,根据 header中文件名称和 在返回reponse中的 文件流 , 转换成BufferedInputStream写入本地文件或用作其他。
public static void main(String[] args) {
String result = null;
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet HttpGet = new HttpGet("http://127.0.0.1:9080/file/download");
// HttpServletResponse response = null;
HttpResponse response = null;
try {
response = httpClient.execute(HttpGet);
// 获取header中文件名称
String fileName = "";
try {
fileName = response.getAllHeaders()[3].getValue().split(";")[1].split("=")[1].substring(1);
System.out.println(fileName);
} catch (Exception e) {
}
// 后去接口返回的文件流
HttpEntity entity = response.getEntity();
BufferedInputStream br = new BufferedInputStream(entity.getContent());
byte[] buf = new byte[1024];
int len = 0;
// 要写入本地的文件
FileOutputStream fileOutputStream = new FileOutputStream("/Users/jjshen/bysj/" + fileName);
while ((len = br.read(buf)) != -1) {
fileOutputStream.write(buf, 0, len);
}
fileOutputStream.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
httpClient.close();
// response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
二、提供“远程文件” ,给浏览器、前端Ajax或其他开发者接口来下载。
其实和 “第一点” 一样, 就是获取文件源的地方稍作修改。在“第一”点中是 直接 new File(filePath); 一个文件出来,转换成字节 流,放入 response中。
2.1、从 Ftp/SFtp上获取的
其实就是 使用 FTPClient 登陆到文件所在目录 ,然后将文件转换成一个 输出流OutputStream或Byte[]。
首先文件名要知道。ftp/sft的地址 登陆用户名/密码等。
public static void downloadFile(HttpServletRequest request, HttpServletResponse response, String fileName)
throws IOException {
// 本地文件地址,文件名称,我是在本机运行,如果是服务器的话,地址可能是 ../fileServer/file/deploy2.sh
//String filePath = "/Users/jjshen/bysj/deploy2.sh";
// String fileName = "deploy2.sh";
int reply;
FTPClient ftp = new FTPClient();
// 获取浏览器的信息
String agent = request.getHeader("USER-AGENT");
if (agent != null && agent.toLowerCase().indexOf(FIRE_FOX) > 0) {
//火狐浏览器自己会对URL进行一次URL转码所以区别处理
response.setHeader("Content-Disposition",
"attachment; filename=" + new String(fileName.getBytes("GB2312"), "ISO-8859-1"));
} else if (agent.toLowerCase().indexOf(SAFARI) > 0) {
//苹果浏览器需要用ISO 而且文件名得用UTF-8
response.setHeader("Content-Disposition",
"attachment; filename=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
} else {
//其他的浏览器
response.setHeader("Content-Disposition",
"attachment; filename=\"" + java.net.URLEncoder.encode(fileName, "UTF-8"));
}
ftp.connect("http://xxx.xxx.xxx", 22);
//下面三行代码必须要,而且不能改变编码格式
ftp.setControlEncoding("GBK");
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
conf.setServerLanguageCode("zh");
//如果采用默认端口,可以使用ftp.connect(url) 的方式直接连接FTP服务器
ftp.login("root", "123456");//登录
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return success;
}
String remotePath = "/home/username/files/";
ftp.changeWorkingDirectory(remotePath+"/");//转移到FTP服务器目录
FTPFile[] fs = ftp.listFiles();
OutputStream outputStream = response.getOutputStream();
for(int i = 0; i < fs.length; i++){
FTPFile ff = fs[i];
if(ff.getName().equals(fileName)){
// System.out.println("fileName"+fileName);
String filename1 = URLEncoder.encode(fileName,"utf-8");
response.setHeader("Content-disposition","attachment;filename="+URLEncoder.encode(fileName,"utf-8"));
//将文件保存到输出流outputStream中
ftp.retrieveFile(new String(ff.getName().getBytes("GBK"),"ISO-8859-1"), outputStream);
outputStream.flush();
outputStream.close();
break;
}
}
ftp.logout();
success = true;
ftp.disconnect();
}
上面是使用 FtpClient.retrieveFile 直接将Ftp上文件写入response 的outputStream中。其实 FtpClient.retrieveFile 的效率没有 ftpClient.retrieveFileStream方法快。 如果考虑效率这里可以将retrieveFile 方法修改成 retrieveFileStream,然后再将返回结果写入 outputStream 中去。
public static void downloadFile(HttpServletRequest request, HttpServletResponse response, String fileName)
throws IOException {
// 本地文件地址,文件名称,我是在本机运行,如果是服务器的话,地址可能是 ../fileServer/file/deploy2.sh
//String filePath = "/Users/jjshen/bysj/deploy2.sh";
// String fileName = "deploy2.sh";
int reply;
FTPClient ftp = new FTPClient();
// 获取浏览器的信息
String agent = request.getHeader("USER-AGENT");
if (agent != null && agent.toLowerCase().indexOf(FIRE_FOX) > 0) {
//火狐浏览器自己会对URL进行一次URL转码所以区别处理
response.setHeader("Content-Disposition",
"attachment; filename=" + new String(fileName.getBytes("GB2312"), "ISO-8859-1"));
} else if (agent.toLowerCase().indexOf(SAFARI) > 0) {
//苹果浏览器需要用ISO 而且文件名得用UTF-8
response.setHeader("Content-Disposition",
"attachment; filename=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
} else {
//其他的浏览器
response.setHeader("Content-Disposition",
"attachment; filename=\"" + java.net.URLEncoder.encode(fileName, "UTF-8"));
}
ftp.connect("http://xxx.xxx.xxx", 22);
//下面三行代码必须要,而且不能改变编码格式
ftp.setControlEncoding("GBK");
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
conf.setServerLanguageCode("zh");
//如果采用默认端口,可以使用ftp.connect(url) 的方式直接连接FTP服务器
ftp.login("root", "123456");//登录
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return success;
}
String remotePath = "/home/username/files/";
OutputStream outputStream= response.getOutputStream();
ftp.changeWorkingDirectory(remotePath+"/");//转移到FTP服务器目录
InputStream inputStream = ftpClient.retrieveFileStream(new String(fileName.getBytes("GBK"), "ISO-8859-1"));
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf, 0, buf.length)) > 0) {
outputStream.write(buf, 0, len);
}
inputStream.close();
outputStream.flush();
outputStream.close();
ftp.logout();
ftp.disconnect();
}
2.2、从接口中获取
其实就相当于 在1.4调用接口成功后,将文件流,在提供给返回给浏览器、前端页面、接口等。
------------------------------------------------------分割线---------------------------------------------------------------------------
博客只作为博主记录、复习用,如果有什么不足之处或更好的建议,都可以一起畅快的交流。对你有用或拿来参考的话,请自行完善,有任何不足或引发的事故概不负责。