SFTP文件上传下载以及如何处理异常,页面展现
首先需要一个第三方包,网上有很多种方式,我这里用的是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>
人比较懒,目前只有下载需求,上传直接往文件服务器丢,后续会完善上传的部分代码。
遇到的坑:
为什么不直接输出文件,要用一个临时文件:核心原因就是我们的服务器地址、用户密码都是由管理员在系统中进行设置,而不是配置文件。而且文件服务器宕机,这里也需要给出一些错误信息。
不用临时文件这些错误是捕获不到的。我们用临时文件,一切正常之后再把临时文件瞧瞧删掉~