首先需要一个第三方包,网上有很多种方式,我这里用的是ChannelSftp
API地址 http://epaul.github.io/jsch-documentation/javadoc/com/jcraft/jsch/ChannelSftp.html
1.工具类
/**
*
* @author Caicaizi
*
*/
public class SFTPUtils {
private static final Logger LOG = LoggerFactory.getLogger(SFTPUtils.class);
/**
* 打开ssh会话
*
* @param username
* @param host
* @param port
* @param pwd
* @return
* @throws JSchException
*/
private static Session openSession(String username, String host, int port, String pwd) throws JSchException {
JSch jsch = new JSch();
Session session = jsch.getSession(username, host, port);
session.setPassword(pwd);
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
config.put("PreferredAuthentications", "password");
int timeout = 3000;
session.setServerAliveInterval(timeout); //
session.setConfig(config); // 为Session对象设置properties
session.setTimeout(timeout); // 设置timeout时间
session.connect(); // 通过Session建立连接
LOG.info("Session connected.");
return session;
}
/**
* 获取文件
*
* @param filePath
* 文件路径
* @param out
* 输出流
* @param username
* @param host
* @param port
* @param pwd
*/
public static void getFile(String filePath, OutputStream out, String username, String host, int port, String pwd) {
ChannelSftp channel = null;
Session session = null;
try {
session = openSession(username, host, port, pwd);
LOG.info("Opening channel");
channel = (ChannelSftp) session.openChannel("sftp");
channel.connect();
channel.get(filePath, out);
} catch (JSchException e) {
LOG.error("连接服务器失败", e);
} catch (SftpException e) {
LOG.error("文件获取失败", e);
} finally {
close(channel, session);
}
}
public static void uploadFile(InputStream input, String filename, String path, String username, String host,
int port, String pwd) {
ChannelSftp channel = null;
Session session = null;
try {
session = openSession(username, host, port, pwd);
channel = (ChannelSftp) session.openChannel("sftp");
channel.connect();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
String date = format.format(new Date());
channel.cd(path);
// 查看日期对应的目录状态
try {
channel.stat(date);
} catch (Exception e) {
LOG.info("目录不存在,创建目录:" + path + "/" + date);
channel.mkdir(date);
}
channel.put(input,path+"/"+date+"/"+filename);
} catch (JSchException e) {
LOG.error("连接服务器失败", e);
} catch (SftpException e) {
LOG.error("文件上传失败", e);
} finally{
close(channel, session);
}
}
/**
* 释放sftp连接资源
*
* @param channel
* @param session
*/
private static void close(ChannelSftp channel, Session session) {
if (channel != null) {
channel.quit();
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
}
}
我们所需要的参数必须:IP,PORT,路径,文件名,以及账号密码;
上传文件可以直接调用:uploadFile,下载:getFile。
注意:下载时用户只需要有读权限,但进行文件上传用户必须有文件夹写的权限
2.servlet
先考虑发生的错误:文件没下载下来,1.可能是文件服务器挂了,2.也可能是文件路径有问题(博主的文件服务器是由系统管理员在应用中配置的,并不固定也有可能填错);
考虑到上面的错误,需要在页面给用户错误提示
public class FileServlet extends HttpServlet {
/**
* post方式下载
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 编码
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
// 要下载的文件名
String filename = request.getParameter("filename");
// 最终相应输出流
OutputStream out = response.getOutputStream();
// 临时输出流
FileOutputStream outputStream = null;
// 输入流
FileInputStream inputStream = null;
// 临时文件
File file = null;
// 临时文件名称,用UUID,输出到浏览器后,删掉这个临时文件
try {
String uuid = UUIDGenerator.generate();
File fileDir = new File("./tmp");
if (!fileDir.exists()) {
fileDir.mkdirs();
}
file = new File(fileDir, uuid);
outputStream = new FileOutputStream(file);
// 用工具类把文件到输出到临时文件
SFTPUtils.getFile(filename, outputStream, "username", "host", 80, "pwd");
// 设置下面两个响应头,在web端返回的就是下载结果
// URLEncoder.encode(filename, "UTF-8")解决中文乱码的问题
response.addHeader("Content-type", "application/octet-stream");
response.addHeader("Content-Disposition", "attachment:filename=" + URLEncoder.encode(filename, "UTF-8"));
// 把临时文件copy到response的相应中
inputStream = new FileInputStream(fileDir);
// 这里用了apache.commoms.io的工具类
IOUtils.copy(inputStream, out);
} catch (JSchException e) {
Log.error("FTP连接失败",e);
}catch(SftpException e){
Log.error("配置路径错误,文件未找到",e);
}finally{
// 关闭流,删除临时文件
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
if(file.exists()){
file.delete();
}
}
}
}
3。页面
为什么不直接用<a>标签?
老大就要在页面上挂个中文,用a在后台会乱码,我也尝试过在后台转码,好几种方式,但是都失败了,另外一个就是错误提示了,下载失败怎么搞?所以用了form表单,用post的方式进行提交
通过js document.getElementId('file_form').submit()进行表单提交
<span id='file'>下载一个中文文件</span> <form method='post' action='FileServlet' target="_blank" id='file_form'> <input type='hidden' name='filename' value='一个中文文件.pdf'> </form>
人比较懒,目前只有下载需求,上传直接往文件服务器丢,后续会完善上传的部分代码。
遇到的坑:
为什么不直接输出文件,要用一个临时文件:核心原因就是我们的服务器地址、用户密码都是由管理员在系统中进行设置,而不是配置文件。而且文件服务器宕机,这里也需要给出一些错误信息。
不用临时文件这些错误是捕获不到的。我们用临时文件,一切正常之后再把临时文件瞧瞧删掉~