java--文件下载
总览
因为公司使用了文件服务器,所以记录一下文件的下载,一个是单个文件下载。在单个文件下载下形成多个文件以压缩包的形式下载。
单个文件下载
流程是从文件服务器上下载文件到项目环境上,然后再传输给前端浏览器:
public void downLoadFile() {
//项目环境下的request和response
HttpServletRequest request = (HttpServletRequest) RpcContext.getContext().getRequest();
HttpServletResponse response = (HttpServletResponse) RpcContext.getContext().getResponse();
String fileName = request.getParameter("fileName");
//用于识别下载文件的类型,主要是环境中配置了不同文件不同路径。
String fileType = request.getParameter("flag");
LOGGER.info("文件的类型!"+fileType);
LOGGER.info("文件的名称!"+fileName);
String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
LOGGER.info("文件下载接口最开始入参为:fileName="+fileName);
//根据不同的文件类型获取到不同的文件服务器地址目录
String serverPath = getServerPath(fileType);
//根据不同的文件类型获取到不同的本地目录
String localPath = getLocalPath(fileType);
try {
//文件服务器目录
String remotePath = basePath + serverPath;
FTPUtils ftpUtils = new FTPUtils();
//下载图片到服务器
//ftpIp为fip地址
boolean downloadFile = ftpUtils.downloadFile(ftpIp, port, username, password, remotePath, fileName, localPath);
if(downloadFile){
LOGGER.info("下载到本地服务器成功!");
String path = localPath +"/"+ fileName;
LOGGER.info("在服务器的路径为:"+path);
String downLoadName = fileName.substring(0, fileName.indexOf("."));
downLoadName = URLEncoder.encode(downLoadName, "UTF-8");
LOGGER.info("转码后的名称为:"+downLoadName);
LOGGER.info("设置响应头,控制浏览器下载该文件!");
response.setHeader("content-disposition", "attachment;filename=" +(downLoadName + fileSuffix));
LOGGER.info("响应头成功");
// 读取要下载的文件,保存到文件输入流
FileInputStream in = new FileInputStream(path);
LOGGER.info("图片输入流为:"+in);
// 创建输出流
LOGGER.info("创建输出流!");
OutputStream out = response.getOutputStream();
// 创建缓冲区
byte buffer[] = new byte[1024];
int len = 0;
// 循环将输入流中的内容读取到缓冲区当中
LOGGER.info("开始输出!");
while ((len = in.read(buffer)) > 0) {
// 输出缓冲区的内容到浏览器,实现文件下载
out.write(buffer, 0, len);
}
LOGGER.info("输出结束!");
// 关闭文件输入流
in.close();
// 关闭输出流
out.close();
File localFile = new File(localPath + File.separatorChar + fileName);
boolean flag = localFile.delete();
if(flag){
LOGGER.info("传输完成,成功删除文件:{}",fileName);
out.close();
}
}
} catch (Exception e) {
LOGGER.error("文件下载接口异常为:"+ e.getMessage());
}
}
- 其中ftpUtils类中单文件和多文件下载区别并不大,下载文件方法为:
public class FTPUtils {
/**
* Description: 从FTP服务器下载文件
*
* @param host FTP服务器hostname
* @param port FTP服务器端口
* @param username FTP登录账号
* @param password FTP登录密码
* @param remotePath FTP服务器上的相对路径
* @param fileName 要下载的文件名
* @param localPath 下载后保存到本地的路径
* @return
*/
public boolean downloadFile(String host, int port, String username, String password, String remotePath,
String fileName, String localPath) {
boolean result = false;
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(host, port);
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
ftp.login(username, password);// 登录
reply = ftp.getReplyCode();
LOGGER.info("FTP登录成功!");
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
LOGGER.error("下载文件异常,ftp服务连接失败!");
return result;
}
if (FTPReply.isPositiveCompletion(ftp.sendCommand("OPTS UTF8", "ON"))) {
LOCAL_CHARSET = "UTF-8";
}
ftp.setControlEncoding(LOCAL_CHARSET);
ftp.setFileType(FTP.BINARY_FILE_TYPE);
ftp.enterLocalPassiveMode();
ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
FTPFile[] fs = ftp.listFiles();
LOGGER.info("FTP目录上文件个数====================="+fs.length);
File file = new File(localPath);
if (!file.exists()) { // 判断文件目录存在不,不存在则创建
file.mkdirs();
}
LOGGER.info("localPath================="+localPath);
for (FTPFile ff : fs) {
if (ff.getName().equals(fileName)) {
LOGGER.info("ff.getName================="+ff.getName());
LOGGER.info("fileName================="+fileName);
File localFile = new File(localPath + "/" + ff.getName());
LOGGER.info(localPath+ "/" +ff.getName());
if (!localFile.exists()) { // 判断文件存在不,不存在则创建
localFile.createNewFile();
}
LOGGER.info("将ftp上图片转为流!");
OutputStream is = new FileOutputStream(localFile);
//ftp.retrieveFile(ff.getName(), is);
LOGGER.info("下载图片到服务器成功!");
String ffp=new String(ff.getName().getBytes(LOCAL_CHARSET),SERVER_CHARSET);
ftp.retrieveFile(ffp , is);
LOGGER.info("下载的fileName!"+ffp);
is.close();
break;
}
}
ftp.logout();
LOGGER.info("下载成功!");
result = true;
} catch (IOException e) {
LOGGER.error("下载文件异常{}", e);
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}
}
/**
* Description: 从FTP服务器下载文件
*
* @param host FTP服务器hostname
* @param port FTP服务器端口
* @param username FTP登录账号
* @param password FTP登录密码
* @param remotePath FTP服务器上的相对路径
* @param fileName 要下载的文件名,为一个List
* @param localPath 下载后保存到本地的路径
* @return
*/
public boolean downloadFileList(String host, int port, String username, String password, String remotePath,
String[] fileName, String localPath) {
boolean result = false;
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(host, port);
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
ftp.login(username, password);// 登录
reply = ftp.getReplyCode();
LOGGER.info("FTP登录成功!");
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
LOGGER.error("下载文件异常,ftp服务连接失败!");
return result;
}
if (FTPReply.isPositiveCompletion(ftp.sendCommand("OPTS UTF8", "ON"))) {
LOCAL_CHARSET = "UTF-8";
}
ftp.setControlEncoding(LOCAL_CHARSET);
ftp.setFileType(FTP.BINARY_FILE_TYPE);
ftp.enterLocalPassiveMode();
ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
FTPFile[] fs = ftp.listFiles();
LOGGER.info("FTP目录上文件个数====================="+fs.length);
File file = new File(localPath);
if (!file.exists()) { // 判断文件目录存在不,不存在则创建
file.mkdirs();
}
LOGGER.info("localPath================="+localPath);
for (FTPFile ff : fs) {
for (String filename: fileName ) {
if (ff.getName().equals(filename)) {
LOGGER.info("ff.getName================="+ff.getName());
LOGGER.info("fileName================="+filename);
File localFile = new File(localPath + "/" + ff.getName());
LOGGER.info(localPath+ "/" +ff.getName());
if (!localFile.exists()) { // 判断文件存在不,不存在则创建
localFile.createNewFile();
}
LOGGER.info("将ftp上图片转为流!");
OutputStream is = new FileOutputStream(localFile);
//ftp.retrieveFile(ff.getName(), is);
LOGGER.info("下载图片到服务器成功!");
String ffp=new String(ff.getName().getBytes(LOCAL_CHARSET),SERVER_CHARSET);
ftp.retrieveFile(ffp , is);
LOGGER.info("下载的fileName!"+ffp);
is.close();
break;
}
}
}
ftp.logout();
LOGGER.info("下载成功!");
result = true;
} catch (IOException e) {
LOGGER.error("下载文件异常{}", e);
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}
多文件下载,并生成压缩包
@GET
@Path("downLoadFileList")
public void batchDownload(@Context HttpServletRequest request, @Context HttpServletResponse response) {
String fileName = request.getParameter("fileName");
String[] fileNames = fileName.split(",");
String fileType = request.getParameter("flag");
List<File> files = new ArrayList<>();
String serverPath = getServerPath(fileType);
//项目服务器存储地址
String localPath = getLocalPath(fileType);
LOGGER.info("循环下载多个文件的个数:fileSize="+ fileNames.length);
// String fileSuffix = filename.substring(filename.lastIndexOf("."));
// LOGGER.info("文件下载接口最开始入参为:fileName="+filename);
try {
//FTP服务器上的相对路径
String remotePath = basePath + serverPath;
FTPUtils ftpUtils = new FTPUtils();
//下载图片到服务器
boolean downloadFile = ftpUtils.downloadFileList(ftpIp, port, username, password, remotePath, fileNames, localPath);
if(downloadFile){
for (String name: fileNames ) {
LOGGER.info("文件下载到本地服务器成功!");
String path = localPath +"/"+ name;
LOGGER.info("文件在服务器的路径为:"+path);
//多个文件
files.add(new File(path));
}
}
} catch (Exception e) {
e.printStackTrace();
}
File zipPath = new File(localPath+"/"+(int)((Math.random()*9+1)*1000));
if (!zipPath.exists()) { // 判断文件目录存在不,不存在则创建
try {
zipPath.createNewFile();
} catch (Exception e) {
e.printStackTrace();
}
}
zipFiles(request, response,files,zipPath);
}
/**
*压缩文件并输出到response响应头中
* @param response
* @param fileList 多文件列表
* @param zipPath 压缩的文件暂存的目录,下载后会删除掉
*/
public static void zipFiles(HttpServletRequest request,HttpServletResponse response, List<File> fileList, File zipPath) {
// 1 文件压缩
FileOutputStream fileOutputStream=null;
ZipOutputStream zipOutputStream=null;
FileInputStream fileInputStream=null;
try {
LOGGER.info("文件本地服务器目录!");
fileOutputStream=new FileOutputStream(zipPath); // 实例化 FileOutputStream对象
zipOutputStream=new ZipOutputStream(fileOutputStream); // 实例化 ZipOutputStream对象
ZipEntry zipEntry=null; // 创建 ZipEntry对象
LOGGER.info("文件压缩个数:"+fileList.size());
for (int i=0; i<fileList.size(); i++) { // 遍历源文件数组
fileInputStream = new FileInputStream(fileList.get(i)); // 将源文件数组中的当前文件读入FileInputStream流中
zipEntry = new ZipEntry(fileList.get(i).getName()); // 实例化ZipEntry对象,源文件数组中的当前文件
zipOutputStream.putNextEntry(zipEntry);
int len; // 该变量记录每次真正读的字节个数
byte[] buffer=new byte[1024]; // 定义每次读取的字节数组
while ((len=fileInputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, len);
}
}
zipOutputStream.closeEntry();
zipOutputStream.close();
fileInputStream.close();
fileOutputStream.close();
// 2 文件下载
long currentTime=System.currentTimeMillis(); // 当时时间戳
int randomFour=(int)((Math.random()*9+1)*1000); // 4位随机数
String fileName=String.valueOf(currentTime)+String.valueOf(randomFour)+".zip"; // 新的文件名称
String path=zipPath.toString();
// 设置输出的格式
response.setHeader("Content-Disposition", "attachment;filename=\""+ encodeFilename(request,fileName)+"\"");
LOGGER.info("压缩文件名:"+fileName);
response.setContentType("application/x-download");//告知浏览器下载文件,而不是直接打开,浏览器默认为打开
// 循环取出流中的数据
FileInputStream inStream=new FileInputStream(path); // 读到流中
byte[] b = new byte[100];
int len;
try {
OutputStream os=response.getOutputStream();
while ((len = inStream.read(b)) > 0){
os.write(b, 0, len);
}
inStream.close();
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try { // 3 删除压缩包
String path=zipPath.toString();
File zfile = new File(path);
zfile.delete();
for (File file:fileList){
file.delete();
}
} catch (Exception e){
e.printStackTrace();
}
}
}
遇到的问题
在测试的时候,文件名的传输顺序要在前面,不然会访问不到。