导语:
昨天接到项目经理这么一个需求,让我在POI导出Excel的时候写一份到我之前搭建的ftp服务器上。所以就有了这篇博客首先我们来分析下之前的业务逻辑:我们创建并构造了一个workbook,然后构建了一个OutputStream输出流,然后我们把数据写入输出流中就可以被客户端下载。
现在我们要在此基础上写一份到ftp服务器
那么我们就需要两个流,首先一个输入流把文件写到ftp服务器,然后需要一个输出流把文件输出到客户端。千万不要用workbook.write(out)一份到客户端,然后又workbook.write(in)一份到ftp,会报错的。Stream is closed,(为啥报错,我也解释不清希望大神解惑一下)
正确思路应该是先写一份到ftp服务器,然后再读取这个文件,然后再把这个文件输出给客户端
参考:
来看代码吧:
1. 我们只需要对 oi实现百万级数据导出 中的 CommentController 稍作修改,然后配合一些工具类就可以实现
/** * excel导出功能 * @param commentSearch * @param response * @param request * @return * @throws Exception */ @RequestMapping("/exportCommentInfo") @ResponseBody @NoRepeatRequest public BaseDTO exportCommentInfo(CommentSearch commentSearch, HttpServletResponse response, HttpServletRequest request) throws Exception{ LOGGER.info("CommentController.exportCommentInfo start"); long startTime = System.currentTimeMillis(); LOGGER.info("开始下载........................................."); List<ErrorInfo> errors = null; int result = 0; String fileName = FileNameUtils.getExportCommontExcelFileName(); OutputStream fileOut = null; SXSSFWorkbook workbook = null; try { LOGGER.debug("classpath: " + fileName); workbook = new SXSSFWorkbook(10000); commentService.exportCommentInfo(request,workbook, commentSearch); // 定义excel文件名 response.setCharacterEncoding("UTF-8"); response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\""); // 定义输出流 fileOut = response.getOutputStream(); // 调用导出方法 //workbook.write(fileOut); //写一份到ftp服务器 ByteArrayOutputStream os = new ByteArrayOutputStream(); workbook.write(os); byte[] b = os.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(b); FtpUtil.uploadFileToFtp(fileName,in,fileOut); workbook.dispose(); } catch (Exception e) { LOGGER.error("InterfaceInfoController.exportInterfaceInfo Exception: ", e); ErrorInfo errorInfo = new ErrorInfo("system.error", "系统异常!"); errors = Arrays.asList(errorInfo); request.getSession().setAttribute("exportStatus","error"); }finally { fileOut.close(); workbook.close(); } LOGGER.info("下载完成....|||||.......用时:" + (System.currentTimeMillis() - startTime)); return tranferBaseDTO(errors, result); }
1.首先把workbook写到 ByteArrayOutputStream 输出流中,然后转换成 字节数组 byte[] b 在转换成 ByteArrayInputStream 输入流用来写入ftp
ByteArrayOutputStream os = new ByteArrayOutputStream(); workbook.write(os); byte[] b = os.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(b); FtpUtil.uploadFileToFtp(fileName,in,fileOut);
2.然后转换成 字节数组 在转换成输入流用来写入ftp ,主要看这几个方法
/** * 上传重载 二进制流 * @param filename * @param input * @return */ public static boolean uploadFileToFtp(String filename, ByteArrayInputStream input, OutputStream fileOut) { getPropertity(); return uploadFileToFtp( host, port, username, password, basePath, filePath, filename, input, fileOut); } /** * Description: 向FTP服务器上传文件 二进制流文件 * @param host FTP服务器hostname * @param port FTP服务器端口 * @param username FTP登录账号 * @param password FTP登录密码 * @param basePath FTP服务器基础目录 * @param filePath FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath * @param filename 上传到FTP服务器上的文件名 * @return 成功返回true,否则返回false */ public static boolean uploadFileToFtp(String host, String port, String username, String password, String basePath, String filePath, String filename, ByteArrayInputStream input, OutputStream fileOut) { FTPClient ftp = new FTPClient(); try { //开启ftp连接 boolean result = connectFtp(ftp, host, port, username, password, basePath, filePath); if(!result){ return result; } if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) { // 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK). if (FTPReply.isPositiveCompletion(ftp.sendCommand("OPTS UTF8", "ON"))) { LOCAL_CHARSET = "UTF-8"; } } //防止中文乱码 filename = new String(filename.getBytes(LOCAL_CHARSET),SERVER_CHARSET ); //为了加大上传文件速度,将InputStream转成BufferInputStream , InputStream input BufferedInputStream in = new BufferedInputStream(input); //加大缓存区 ftp.setBufferSize(1024*1024); //设置上传文件的类型为二进制类型 ftp.setFileType(FTP.BINARY_FILE_TYPE); //上传文件 if (!ftp.storeFile(filename, in)) { return false; } //写一份给客户端 FTPFile[] fs = ftp.listFiles(); for (FTPFile ff : fs) { if (ff.getName().equals(filename)) { ftp.retrieveFile(ff.getName(), fileOut); fileOut.close(); } } in.close(); ftp.logout(); } catch (IOException e) { e.printStackTrace(); } finally { if (ftp.isConnected()) { try { ftp.disconnect(); } catch (IOException ioe) { } } } return true; } /** * 连接ftp服务器并切换到目的目录 * 调用此方法需手动关闭ftp连接 * @param ftp * @param host * @param port * @param username * @param password * @param basePath * @param filePath * @return */ private static boolean connectFtp( FTPClient ftp,String host, String port, String username, String password, String basePath, String filePath){ boolean result = false; try { int portNum = Integer.parseInt(port); int reply; // 连接FTP服务器 ftp.connect(host, portNum); // 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器 ftp.login(username, password); reply = ftp.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { ftp.disconnect(); return result; } //切换到上传目录 if (!ftp.changeWorkingDirectory(basePath+filePath)) { //如果目录不存在创建目录 String[] dirs = filePath.split("/"); String tempPath = basePath; for (String dir : dirs) { if (null == dir || "".equals(dir)) { continue; } tempPath += "/" + dir; if (!ftp.changeWorkingDirectory(tempPath)) { if (!ftp.makeDirectory(tempPath)) { return result; } else { ftp.changeWorkingDirectory(tempPath); } } } } result = true; } catch (IOException e) { e.printStackTrace(); } return result; } }
主要是 我们为了加快文件上传速度把刚才传入的 ByteArrayInputStream 转换成了 BufferedInputStream
BufferedInputStream in = new BufferedInputStream(input);
然后我们把 BufferedInputStream 写入到ftp服务器
ftp.storeFile(filename, in)
到这里第一步就完成了,接下来是从服务器下载我们刚才上传 文件,然后通过刚才传入的 输出流 fileOut 输出到客户端
//写一份给客户端 FTPFile[] fs = ftp.listFiles(); for (FTPFile ff : fs) { if (ff.getName().equals(filename)) { ftp.retrieveFile(ff.getName(), fileOut); fileOut.close(); }}
原文地址:https://www.cnblogs.com/UncleWang001/p/10115427.html