InputStream需要被多次使用的解决方案
先把需求甩出来,我有两台SFTP服务器A、B,我需要从A中取出一批文件,上传到B中的目录①并且要备份到目录②,所以从A中我会得到一批InputStream。
这个时候,为了效率我可能会close掉这个到A的连接,这里如果close掉了,那么这个流就消失了。
还有假设我没有close掉A连接,那么当我将InputStream给put到B的目录①之后,继续put到B的备份目录②,这个时候你会发现,文件确实都上传上去了,但是目录②的文件大小是0k。
这是因为InputStream的特性,可以把他想成一个指针,每读取一个字节指针就向后移一次,整个文件读完,指针已经移到最后了,当下次再读的时候,是从InputStream末尾开始读的,也就什么都读不到,造成了0k的情况。
有人可能想将InputStream复制一份,很不幸的告诉你,InputStream不能被复制,就算复制了也没有效果,因为他本身不存储数据,他只是建立了一个流向文件的数据流,所以关掉连接的话从InputStream中就读不到文件了,可以把他理解成一个管道。
下面提供一种解决办法,将InputStream中的内容缓存到ByteArrayOutputStream这里面,ByteArrayOutputStream是可以被复制的,也就是在读取InputStream之后,将他缓存到ByteArrayOutputStream,那么这个时候即时关闭连接,我的内容也已经缓存到了ByteArrayOutputStream中,思想就是这有,下面直接看代码。
下面是一个实现上述需求的完整代码
package com.baozun.store.manager.tafile;import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;import com.baozun.nebula.utilities.common.ProfileConfigUtil;
import com.baozun.store.util.SFTPUtils;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.SftpException;/*** 上传tafile文件* * @author sunchenbin* @version 2015年11月27日 下午2:59:48*/
@Service("cogradientTafileManager")
public class CogradientTafileManager{private static final Logger log = LoggerFactory.getLogger(CogradientTafileManager.class);private ChannelSftp sftpClient1 = null;private SFTPUtils sftp1 = new SFTPUtils();private ChannelSftp sftpClient2 = null;private SFTPUtils sftp2 = new SFTPUtils();private Properties properties = ProfileConfigUtil.findPro("config/sftp.properties");private String FROM_BASEPATH = properties.getProperty("from.sftp.basePath"); // 读取文件的基本路径private final String FROM_IP = properties.getProperty("from.sftp.ip"); // 读取文件的服务器IP地址private final String FROM_USERNAME = properties.getProperty("from.sftp.username"); // 读取文件的用户名private final String FROM_USERPWD = properties.getProperty("from.sftp.userpwd"); // 读取文件的密码private final String FROM_PORT = properties.getProperty("from.sftp.port"); // 读取文件的端口号private String TO_BASEPATH = properties.getProperty("to.sftp.basePath"); // 上传文件的基本路径private String TO_BACKUP_BASEPATH = properties.getProperty("to.sftp.backUpbasePath"); // 上传文件的备份路径private final String TO_IP = properties.getProperty("to.sftp.ip"); // 上传文件的服务器IP地址private final String TO_USERNAME = properties.getProperty("to.sftp.username"); // 上传文件的用户名private final String TO_USERPWD = properties.getProperty("to.sftp.userpwd"); // 上传文件的密码private final String TO_PORT = properties.getProperty("to.sftp.port"); // 上传文件的端口号private final String TA_MEM = "TA_MEM"; // 文件前缀private final String TA_NOM = "TA_NOM"; // 文件前缀private final String BACKUP = "backUp/"; // 创建备份文件夹/*** 将InputStream中的字节保存到ByteArrayOutputStream中。*/private ByteArrayOutputStream byteArrayOutputStream = null;/*** 需要执行的job方法*/public void doExcuteJob(){// 读取Map<String, ByteArrayOutputStream> inputStreamMap = buildFileMap();// 上传uploadTaFile(inputStreamMap);}/*** 上传tafile文件到sftp* * @param inputStreamMap*/private void uploadTaFile(Map<String, ByteArrayOutputStream> inputStreamMap){// 连接上传文件的sftpsftpClient2 = sftp2.connect(TO_IP, Integer.parseInt(TO_PORT), TO_USERNAME, TO_USERPWD);log.info(TO_IP + " 连接成功");if(inputStreamMap.size() > 0){log.info("正在准备上传和备份文件");for (String key : inputStreamMap.keySet()){try{InputStream inputStream1 = new ByteArrayInputStream(inputStreamMap.get(key).toByteArray());InputStream inputStream2 = new ByteArrayInputStream(inputStreamMap.get(key).toByteArray());sftpClient2.put(inputStream1, TO_BASEPATH + key); log.info("上传文件:" + key + " 成功");sftpClient2.put(inputStream2, TO_BACKUP_BASEPATH + key);log.info("上传备份文件:" + key + " 成功");}catch (Exception e){log.error("上传文件失败!");log.error(e.getMessage());}log.info("正在准备删除源文件:"+key);try{sftpClient1.rm(FROM_BASEPATH + key);}catch (SftpException e){log.error("删除源文件失败!");log.error(e.getMessage());}log.info("删除文件完成");}log.info("全部文件上传备份完成");}else {log.info("没有要操作的文件");}sftpClient1.exit();sftpClient2.exit();}/*** 读取sftp文件备份并返回读取的文件* * @return*/private Map<String, ByteArrayOutputStream> buildFileMap(){// 连接读文件的sftpsftpClient1 = sftp1.connect(FROM_IP, Integer.parseInt(FROM_PORT), FROM_USERNAME, FROM_USERPWD);log.info(FROM_IP + " 连接成功");// 存储文件Vector<LsEntry> ftpFiles = null;// TA_MEM和TA_NOM输入流Map<String, ByteArrayOutputStream> inputStreamMap = new HashMap<String, ByteArrayOutputStream>();log.info("正在检查备份文件目录...");// 检查并创建备份目录try{sftpClient1.cd(FROM_BASEPATH + BACKUP);log.info("备份文件目录backUp已存在无需创建");}catch (SftpException e){try{sftpClient1.mkdir(FROM_BASEPATH + BACKUP);log.info("正在创建备份文件目录backUp...");}catch (SftpException e1){log.error("创建备份文件目录backUp失败!");log.error(e1.getMessage());}}// tafile的读取备份并删除源文件try{ftpFiles = sftpClient1.ls(FROM_BASEPATH);if (ftpFiles != null && ftpFiles.size() > 0){for (LsEntry ftpFile : ftpFiles){if (ftpFile.getFilename().startsWith(TA_MEM) || ftpFile.getFilename().startsWith(TA_NOM)){log.info("正在获取TA_MEM和TA_NOM的文件输入流...");inputStreamCacher(sftpClient1.get(FROM_BASEPATH + ftpFile.getFilename()));inputStreamMap.put(ftpFile.getFilename(), byteArrayOutputStream);log.info("正在准备备份文件:"+ftpFile.getFilename());FileCopyUtils.copy(new File(FROM_BASEPATH + ftpFile.getFilename()),new File(FROM_BASEPATH + BACKUP + ftpFile.getFilename()));log.info("备份文件完成");}}}else {log.info("没有需要处理的文件");}}catch (Exception e){log.error("读取文件失败!");log.error(e.getMessage());}return inputStreamMap;}public void inputStreamCacher(InputStream inputStream) {byteArrayOutputStream = new ByteArrayOutputStream();byte[] buffer = new byte[1024]; int len; try {while ((len = inputStream.read(buffer)) > -1 ) { byteArrayOutputStream.write(buffer, 0, len); }} catch (IOException e) {log.error(e.getMessage(), e);}finally{try {byteArrayOutputStream.flush();byteArrayOutputStream.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}
上面的关键代码我在这里列一下:
// 首先是将流缓存到byteArrayOutputStream中
public void inputStreamCacher(InputStream inputStream) {byteArrayOutputStream = new ByteArrayOutputStream();byte[] buffer = new byte[1024]; int len; try {while ((len = inputStream.read(buffer)) > -1 ) { byteArrayOutputStream.write(buffer, 0, len); }} catch (IOException e) {log.error(e.getMessage(), e);}finally{try {byteArrayOutputStream.flush();byteArrayOutputStream.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}// 然后是这里将存好的byteArrayOutputStream取出来做这有的操作,搞两份就好了
InputStream inputStream1 = new ByteArrayInputStream(inputStreamMap.get(key).toByteArray());
InputStream inputStream2 = new ByteArrayInputStream(inputStreamMap.get(key).toByteArray());
到这里基本就实现我们的需求,应该还有其他的实现方式,比如把InputStream的指针还原到初始等等…知道的可以贴出来给大家看看。
InputStream需要被多次使用的解决方案相关推荐
- android outofmemory 原理及解决方案
一.问题描述:Android下的相机在独自使用时,拍照没有问题,通过我们的代码调用时,也正常,但是更换了不同厂商的平板,ROM由Android4.0变成了Android4.1后,拍照出现了Out ...
- 同一Inputstream的父类和子类对象请维持最具体的子类对象,不要混合使用
1 致谢 2 问题描述 今天在进行进行Android编程 使用来源于同一个网络流的两个InputStream对象 即 private BufferedInputStream bis = null; ...
- 位图引起的内存溢出OutOfMemory解决方案
位图引起的内存溢出OutOfMemory解决方案 作者:老帅 一.问题描述:Android下的相机在独自使用时,拍照没有问题,通过我们的代码调用时,也正常,但是更换了不同厂商的平板,ROM由Andro ...
- 写java开头结尾,在Java中向InputStream的开头和结尾添加字符
I have an InputStream which I need to add characters to the beginning and end of, and should end up ...
- 《Thinking in Java》Fourth Edition中文版笔记
第5章 初始化与清理 在Java(和C++)里,构造器是强制重载方法名的另一个原因.既然构造器的名字已经由类名所决定,就只能有一个构造器名. 如果传入的数据类型(实际参数类型)小于方法中声明的形式参数 ...
- 彻底解决Android 拍照 内存溢出 Out of Memory的问题
内存溢出相信做过编程的人都知道一二,这里说Android 内存溢出的问题:.问题描述:Android下的相机在独自使用时,拍照没有问题,通过我们的代码调用时,也正常,但是更换了不同厂商的平板,ROM由 ...
- inputstream流乱码_Java FileInputStream读中文乱码问题解决方案
1.前提 以读取编码是GBK的文件为案例,文件内容只有中文和中文符号 2.原因 FileInputStream读中文乱码是因为一个中文对应两个字节存储(负数),也就是说,读取对应中文的字节数应该是偶数 ...
- java虚拟机资源根目录_Java路径问题最终解决方案—可定位所有资源的相对路径寻址 - java - CSDN技术......
Java路径问题最终解决方案 -可定位所有资源的相对路径寻址 前言 Java的路径问题,非常难搞.最近的工作涉及到创建和读取文件的工作,这里我就给大家彻底得解决Java路径问题. 我编写了一个方法,比 ...
- 转-Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案
前言 刚开始创建MVC与Web API的混合项目时,碰到好多问题,今天拿出来跟大家一起分享下.有朋友私信我问项目的分层及文件夹结构在我的第一篇博客中没说清楚,那么接下来我就准备从这些文件怎么分文件夹说 ...
最新文章
- python使用pickle保存和加载机器学模型
- c++ 绘制函数图像_【图像增强】CLAHE 限制对比度自适应直方图均衡化
- Java中的==和equals区别
- android自定义LinearLayout和View
- IT人员加班处于恐惧之中
- zabbix_appliace 3.4安装部署
- Chromium版Edge体验——几个理由告诉你为什么卸载Chrome!
- C语言 顺时针打印矩阵(二维数组)
- 复旦大学陈平博士:网络攻击猖獗,如何应对数据安全与内生安全挑战?
- 教团1886:高端的半成品电影
- 项目之显示回答和显示评论(13)
- 计算机应用应届求职简历,计算机应用应届生个人简历模板
- css 字符间距,单词间距
- 如何成为一个更好的程序员,或者说是学习者?给你七个建议!
- 从新型冠状病毒想到清单革命
- 《魔灵保卫者》服务端架构及实现
- C语言-结构体函数(录入书本的信息)
- linux find之exec用法
- android 相机闪光灯和手电筒的使用
- 【ARCGIS创建中国南海诸岛及九段线小图框】