使用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);