使用JavaInputStream读取FTP图片到远程服务器
最近需要做一个新需求,要读取ftp服务器的图片,然后保存到另一台服务器上,ftp的访问路径是经过apache转换的,记录一下遇到的坑。我的方案是想办法拿到这张图片的输入流,然后通过
FTPClient
上传到另一台服务器,在网上找到了如何拿到输入流,并保存本地的方法。
Java从网络读取图片通过InputStream保存至本地,代码如下:
package com.bayonet.action;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class Test
{
public static void main(String[] args)
{
InputStream inStream = null;
FileOutputStream outStream = null;
try
{
// 定义一个URL对象,并把http路径放进去
URL urlConet = new URL("http://xxx.xxx.xx.xx/xxx/xxx/P.JPG");
// 打开连接
HttpURLConnection con = (HttpURLConnection) urlConet.openConnection();
// 设置请求方式为GET
con.setRequestMethod("GET");
// 设置超时响应时间为5秒
con.setConnectTimeout(6 * 1000);
// 通过输入流获取图片
inStream = con.getInputStream();
// 定义图片放置绝对路径
String pathName = "D://xxx.JPG";
// 通过将给定路径名字符串转换成抽象路径名来创建一个新 File 实例
File file = new File(pathName);
// 如果路径不存在
if (!file.exists())
{
// 那就创建它
file.mkdirs();
}
// 创建输出流,并把文件路径放置进去
outStream = new FileOutputStream(file);
// 定义缓冲区,即为在内存开的内存空间是1024,也就是一次最多读取1024个字节
byte[] b = new byte[1024];
// 使用输入流从buffer里把数据读取出来 每次读取的字符串长度,如果为-1,代表全部读取完毕
while ((inStream.read(b)) != -1)
{
// 用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度
outStream.write(b);// 写入数据
}
inStream.close();
outStream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if (inStream != null)
{
try
{
//关闭输入流
inStream.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (outStream != null)
{
try
{
//关闭输出流
outStream.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}
Java获取输入流上传图片至FTP服务器,代码如下:
package com.bayonet.action;
import java.io.InputStream;
import java.net.URL;
import com.bayonet.utils.FTPUtil;
public class Test
{
public static void main(String[] args)
{
InputStream inStream = null;
try
{
// 定义一个URL对象,并把http路径放进去
URL urlConet = new URL("http://xxx.xxx.xx.xx/xxx/xxx/P.JPG");
// 通过输入流获取图片
inStream = urlConet.openStream();
// new一个Ftp对象
FTPUtil ftpUtil = new FTPUtil();
// ftp放置文件路径
String ftpPath = "xxx/xxx/xxx";
// 文件名
String fileName = "xxxx.jpg";
boolean uploadFile = ftpUtil.uploadFile(ftpPath, inStream, fileName);
System.out.println(uploadFile);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
package com.bayonet.action;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import com.bayonet.constant.Const;
public class FTPUtil
{
/**
* Ftp 上传图片
*
* @param ftpPath
* ftp路径
* @param inputStream
* 输入流
* @param fileName
* 文件名
* @return boolean
* @throws Exception
* 异常
*/
public boolean uploadFile(String ftpPath, InputStream inputStream, String fileName) throws Exception
{
boolean flag = false;
Exception exp = new Exception();
FTPClient ftpClient = new FTPClient();
try
{
// 连接FTP服务器
ftpClient.connect("FTP连接IP地址","FTP连接端口");
// 登录FTP服务器
ftpClient.login("FTP连接账号", "FTP连接密码");
// 是否成功登录FTP服务器 230为登录成功
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode))
{
return flag;
}
ftpClient.enterLocalPassiveMode();// 开启被动模式
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
boolean makeDir = makeDir(ftpClient, ftpPath);
String printWorkingDirectory = ftpClient.printWorkingDirectory();
System.out.println("获取当前目录:" + printWorkingDirectory);
/*
* storeFile(String remote,InputStream local)抛出IOException 使用给定的名称在服务器上存储一个文件,并从给定的 InputStream 获取输入
* 参数: remote - 给远程文件的名称。 local - 从中读取文件的本地 InputStream。
* 返回:如果成功完成,则为真,否则为假。
*/
ftpClient.storeFile(new String(fileName.getBytes(Const.GBK), Const.ISO), inputStream);
// 通过发送 QUIT 命令注销 FTP 服务器
ftpClient.logout();
flag = true;
}
catch (Exception e)
{
exp = e;
e.printStackTrace();
}
finally
{
if (ftpClient.isConnected())
{
try
{
ftpClient.disconnect();
}
catch (IOException e)
{
exp = e;
throw exp;
}
}
if (inputStream != null)
{
inputStream.close();
}
}
return flag;
}
/**
* ftp创建多级目录(ftp只支持创建一级目录)
*
* @param ftpClient
* ftp连接
* @param path
* 文件路径
* @return boolean
*/
public boolean makeDir(FTPClient ftpClient, String path)
{
boolean makeDirectory = false;
try
{
String[] split = path.split("/");
for (String string : split)
{
if (StringUtils.isEmpty(string))
{
continue;
}
/**
* changeWorkingDirectory 更改 FTP 会话的当前工作目录。
* 返回:如果成功完成,则为真,否则为假
*/
if (!ftpClient.changeWorkingDirectory(string))
{
/**
* makeDirectory 在当前目录(如果给定相对路径名)或指定位置(如果给定绝对路径名)的 FTP 服务器上创建一个新子目录
* 返回:如果成功完成,则为真,否则为假。
*/
makeDirectory = ftpClient.makeDirectory(string);
/**
* changeWorkingDirectory 更改 FTP 会话的当前工作目录。
* 返回: 如果成功完成,则为真,否则为假。
*/
ftpClient.changeWorkingDirectory(string);
}
else
{
makeDirectory = ftpClient.changeWorkingDirectory(string);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return makeDirectory;
}
}
代码看起来是不是感觉没问题?我在本地咋跑咋流畅,到了测试环境,死活跑不通。一直报
java.io.FileNotFoundException: http://xxx.xxx.xx.xx/xxx/xxx/P.JPG
这个错误,我是百思不得其解,在网上找了半天。发现网上的解决方案都不是很靠谱。后来发现我的图片里边有中文名称,我就试着把中文名称去掉,发现居然成功了。于是又到了疯狂设置编码格式的时刻,各种设置发现都不行。后来查看了apache日志才发现,要设置urlEncoder的编码格式为UTF-8.
解决方案:
/*
* java.net.URLEncoderAPI介绍
* 对字符串进行编码时,适用以下规则:
* 字母数字字符“ a”到“ z”、“ A”到“ Z”和“ 0”到“ 9”保持不变。
* 特殊字符“ .”、“ -”、“ *”和“ _”保持不变。
* 空格字符“ ”转换为加号“ +”。
* 所有其他字符都是不安全的,首先使用某种编码方案将其转换为一个或多个字节。然后每个字节由3个字符的字符串“ ”表* * 示,其中xy是字节的两位十六进制表示。推荐使用的编码方案是 UTF-8。但是,出于兼容性原因,如果未指定编码,则使* 用平台的默认编码。 %xy
*/
//根据自己的路径格式 把中文部分截取出来转换格式后,拼起来再放入URl 这样他就能获取到这个图片了
//注:URL的路径必须是http路径
String picPath = java.net.URLEncoder.encode(picPath, "UTF-8");
URL urlConet = new URL(picPath);