注:从(el-link从FTP下载文件在浏览器右上角显示)文章跳转过来的我来解释一下,从ftp上面下载文件牵扯到跨域问题,从而导致download无法生效,翻阅好多文章发现很多最后都是从后端进行处理,来实现下载的ftp文件在浏览器右上角显示的效果。
1.先写一个公共类用来接口调用
因为我的存在ftp上面文件格式为(*/2024-02-05/随机ID.docx),比如zlhz/2024-01-26/6128e6d1-84aa-4dc9-9000-6ab061323770.docx,所以我要对其进行进一步的处理,才能使其在ftp上面找到相对应的文件。
这里的文件类型我给注释掉了
因为我发现在前端调用接口时加个 type: 'application/x-msdownload'即可,这个意思是自动识别文件类型
package com.sjq.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@Slf4j
public class FtpDownload {
private FTPClient ftpClient;
/**
* 实例化
*
* @param hostName FTP服务器地址
* @param port FTP服务器端口
* @param userName FTP登录账户
* @param password FTP登录密码
* @throws IOException
*/
public FtpDownload(String hostName, int port, String userName, String password) throws IOException {
ftpClient = new FTPClient();
//设置传输命令的超时
ftpClient.setDefaultTimeout(20000);//毫秒
//设置两个服务连接超时时间
ftpClient.setConnectTimeout(10000);//毫秒
//被动模式下设置数据传输的超时时间
ftpClient.setDataTimeout(15000);//毫秒
//连接FTP
ftpClient.connect(hostName, port);
//更加账户密码登录服务
ftpClient.login(userName, password);
//被动模式
ftpClient.enterLocalPassiveMode();
}
public Pair<Boolean, String> downloadFile(String ftpFilePath, String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
InputStream input = null;
OutputStream out = null;
//我的存在ftp上面文件格式为(*/2024-02-05/随机ID.docx)
//比如zlhz/2024-01-26/6128e6d1-84aa-4dc9-9000-6ab061323770.docx
//所以我要对其进行进一步的处理
String wjdzStr = URLDecoder.decode(ftpFilePath, "UTF-8");
int index = wjdzStr.indexOf("/"); //获取第一个“/”的位置
index = wjdzStr.indexOf("/", index + 1);//获取第二个 “/” 的位置
String result = wjdzStr.substring(index + 1);//获取第二个 “/” 的位置后面的字符串 (文件名称)
String menuDz = wjdzStr.substring(0,index + 1);//获取第二个 “/” 的位置之前的字符串 (文件目录)
try {
response.reset();
//文件类型
// response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
//解决中文不能生成文件(包含空格)
response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(fileName,"UTF-8").replaceAll("\\+","%20")+"\"");
//解决跨域问题
response.setHeader("Access-Control-Allow-Origin","http://127.0.0.1:8080");
response.setHeader("Access-Control-Allow-Credentials", "true");
//传输模式
ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
// 设置以二进制流的方式传输
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
//进入目录
ftpClient.changeWorkingDirectory(menuDz);
FTPFile[] files = ftpClient.listFiles();
if (files.length < 1) {
return Pair.of(false, "目录为空");
}
boolean fileExist = false;
boolean downloadFlag = false;
for (FTPFile ftpFile : files) {
String ftpFileName=new String(ftpFile.getName().getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
if (result.equals(ftpFileName)) {
fileExist = true;
input = ftpClient.retrieveFileStream(new String(result.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
out = response.getOutputStream();
int len;
byte[] bytes = new byte[1024];
while ((len = input.read(bytes)) != -1) {
out.write(bytes, 0, len);
}
out.flush();
downloadFlag = true;
System.out.println("文件下载成功");
break;
}
}
if (!fileExist) {
return Pair.of(false, "FTP服务器上文件不存在");
}
return Pair.of(downloadFlag, downloadFlag ? "下载成功" : "下载失败");
} catch (IOException e) {
e.printStackTrace();
return Pair.of(false, "下载文件异常");
} finally {
try {
if (out != null) {
out.close();
}
if (input != null) {
input.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void close() {
try {
if (ftpClient != null && ftpClient.isConnected()) {
ftpClient.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.接口调用
这里的FtpUtil ftpUtil = new FtpUtil(),是我自己封装的ftp地址、端口、用户名、密码,使用时改为自己的即可
@GetMapping("/downloadFile")
public String downloadFile(@RequestParam(required = false) String wjdz,
@RequestParam(required = false) String wjmc,
HttpServletResponse response) throws IOException {
FtpDownload ftpDownload = null;
FtpUtil ftpUtil = new FtpUtil();
try {
ftpDownload = new FtpDownload(ftpUtil.hostName,ftpUtil.port, ftpUtil.userName, ftpUtil.password);
Pair<Boolean, String> pair = ftpDownload.downloadFile(wjdz, wjmc, response);
return pair.getRight();
} catch (Exception e) {
log.error("下载异常",e);
return "下载文件异常";
} finally {
if (ftpDownload != null) {
ftpDownload.close();
}
}
}
3.前端调用
下载按钮
<el-link @click="downloadFtp(scope.row.wjdz,scope.row.wjmc)" style="margin-right: 1vw">下载并查看</el-link>
downloadFtp(filePath,fileName) {
this.axios({
url: "/Zlhzjbxxb/downloadFile?wjdz="+filePath+"&wjmc="+fileName,
responseType: 'blob',
method: "GET",
}).then((res) => {
console.log(res);
var elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
var blob = new Blob([res.data], { type: 'application/x-msdownload' });
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
document.body.removeChild(elink);
this.$message.success(`文件下载成功!`)
}).catch((error) => {
this.$message.error(`文件下载失败!`)
});
}
4.最终效果
有一说一,这些东西让我自己写恐怕有点天方夜谭,文章中很多部分都是借鉴他人的,然后自己再去对其简单的缝缝补补,最后把全部整理之后再记录一下。这里有些介绍不详细的地方可以点击下方文章参考进一步了解,如有不妥善的地方还望指正。
文章参考:Java实现FTP下载文件到客户端(浏览器)_java ftp下载文件-CSDN博客
后端返回文件流时,前端如何处理并成功下载流文件以及解决下载后打开显示不支持此文件格式_前端处理application/x-msdownload-CSDN博客