java实现图片直接上传到ftp服务器

工作半年了,之前总是喜欢把总结和学习到的新东西放到自己OneNote上保存,方便自己回顾。但最近翻翻boss直聘上的要求,貌似写技术博客是个加分项哎,思来想去,决定以后但文档都在简上写好了,因为平时查问题的时候发现还是简书的排版最好,废话不说了开始吧!

  • pom.xml依赖引入

  <!-- ftp客户端 -->
    <dependency>
      <groupId>commons-net</groupId>
      <artifactId>commons-net</artifactId>
      <version>3.1</version>
    </dependency>
  • FtpUtil工具类,主要是封装了利用commons.net包下的FTPClient连接、登陆ftp服务器,并切换到ftp服务器下指定文件夹,设置上传到一些属性最后完成上传。其中的ftpIP,ftpUser, ftpPass, ftpPort都是从配置文件properties中读取的。

public class FtpUtil {

    private static Logger logger = LoggerFactory.getLogger(FtpUtil.class);
    //从properties文件读取相应的配置
    private static String ftpIP = PropertiesUtil.getProperty("ftp.server.ip");
    private static String ftpUser = PropertiesUtil.getProperty("ftp.user");
    private static String ftpPass = PropertiesUtil.getProperty("ftp.pass");
    private static String ftpPort = PropertiesUtil.getProperty("ftp.port");

/**
     * 将文件直接上传到ftp服务器到方法,不会经过tomcat本地缓冲
     *
     * @param remotePath  ftp服务器中的文件夹
     * @param fileName    文件的新名称
     * @param inputStream
     * @return
     */
    public static boolean uploadFile(String remotePath, String fileName, InputStream inputStream) {
        boolean isSeccess = false;
        boolean result = false;
        FTPClient ftpClient = new FTPClient();
        try {
            //连接登陆
            ftpClient.connect(ftpIP);
            isSeccess = ftpClient.login(ftpUser, ftpPass);
            logger.info("ftp服务器:{},登陆--{}", ftpIP, isSeccess);
            //如果登陆成功...
            if (isSeccess) {
                //切换到指定的ftp服务器文件夹目录
                boolean isChangeSuccess = ftpClient.changeWorkingDirectory(remotePath);
                //如果切换失败则创建改目录再切换
                if (!isChangeSuccess) {
                    ftpClient.makeDirectory(PropertiesUtil.getProperty("ftp.dir"));
                    ftpClient.changeWorkingDirectory(remotePath);
                }
                //设置缓冲大小,编码格式,文件类型
                ftpClient.setBufferSize(1024);
                ftpClient.setControlEncoding("UTF-8");
                ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
                //开始上传
                logger.info("开始上传文件:{}", fileName);
                result = ftpClient.storeFile(fileName, inputStream);
                logger.info("文件:{},上传:{}", fileName, result);
                return result;
            }
        } catch (IOException e) {
            logger.error("文件上传失败", e);
        } finally {
            try {
                //关闭流
                inputStream.close();
                ftpClient.logout();
            } catch (IOException e) {
                logger.error("输入流关闭失败", e);
            }
        }
        return result;
    }
}
  • Service层主要是对上传对文件进行重命名,并封装好返回给前端的结果样式,其中ServerResponse对象是前端要求的高可用响应对象,附在最后

@Service("iFileService")
public class FileServiceImpl implements IFileService {

    //让控制台输出此文件记录内容
    private Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);

    /**
     * 将文件直接上传至ftp服务器
     *
     * @param file
     * @return
     */
    @Override
    public ServerResponse upload(MultipartFile file) {
        Map<String, String> resultMap = new HashMap();
        String oldName = file.getOriginalFilename();
        String newName = UUID.randomUUID().toString();
        //给文件重命名
        newName = newName + oldName.substring(oldName.lastIndexOf("."));
        try {
            //利用FtpUtil中到方法进行上传文件
            boolean result = FtpUtil.uploadFile(PropertiesUtil.getProperty("ftp.dir"), newName, file.getInputStream());
            if (result) {
                //如果上传成功,则返回前端新的文件名和路径
                resultMap.put("uri", newName);
                //根据nginx配置的ftp文件夹和域名的对应关系组装文件在ftp服务器中的位置
                resultMap.put("url", PropertiesUtil.getProperty("ftp.server.http.prefix") + PropertiesUtil.getProperty("ftp.dir") + newName);
                return ServerResponse.createBySucess(resultMap);
            } else {
                return ServerResponse.createByErrorMessage("上传失败");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ServerResponse.createByErrorMessage("上传失败");
    }
}
  • Controller层逻辑,主要是对用户权限对校验以及文件上传

@RequestMapping(value = "upload", method = {RequestMethod.POST})
    @ResponseBody
    public ServerResponse upload(@RequestParam(value = "file", required = true) MultipartFile file, HttpSession session) {
        User user = (User) session.getAttribute(Const.CURRENT_USER);
        if (user == null) {
            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(), "用户未登陆");
        }
        if (user.getRole() != Const.ADMIN) {
            return ServerResponse.createByErrorMessage("用户无此权限");
        }
        //业务逻辑
        return iFileService.upload(file);
    }

  • 附:高可用响应类
public class ServerResponse<T> implements Serializable {
    private int status;
    private String msg;
    private T data;

    private ServerResponse(int status) {
        this.status = status;
    }

    private ServerResponse(int status, T data) {
        this(status, data, null);
    }

    private ServerResponse(int status, T data, String msg) {
        this.status = status;
        this.data = data;
        this.msg = msg;
    }

    private ServerResponse(int status, String msg) {
        this(status);
        this.msg = msg;
    }
    @JsonIgnore     //json序列化到时候就不会显示这个public方法在json里面
    public boolean isSuccess() {
        return this.status == ResponseCode.SUCCESS.getCode();
    }

    public int getStatus() {
        return status;
    }

    public T getData() {
        return data;
    }

    public String getMsg() {
        return msg;
    }

    //成功时返回到数据封装ServerResponse对象
    public static <T> ServerResponse<T> createBySucess(){
        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode());
    }
    public static <T> ServerResponse<T> createBySucessMsg(String msg){
        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(),msg);
    }
    public static <T> ServerResponse<T> createBySucess(T data){
        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(),data);
    }
    public static <T> ServerResponse<T> createBySucess(String msg,T data){
        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(),data,msg);
    }

    public static <T> ServerResponse<T> createByError(){
        return new ServerResponse<T>(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getDesc());
    }
    public static <T> ServerResponse<T> createByErrorMessage(String msg){
        return new ServerResponse<T>(ResponseCode.ERROR.getCode(),msg);
    }
    public static <T> ServerResponse<T> createByErrorCodeMessage(Integer code,String msg){
        return new ServerResponse<T>(code,msg);
    }
}
  • 附:properties配置文件,其中ftp.server.http.prefix是nginx配置文件中对应ftp文件夹的那个servername
ftp.server.ip=192.168.1.1
ftp.user=ftpuser
ftp.pass=123456
ftp.server.http.prefix=http://image.test.com/
ftp.port=21

#ftp.dir=img/
ftp.dir=video/
  • 这个是conf.d文件夹下自己添加的conf文件,其中的image.test.com对应的是服务器根目录下的ftpfile文件,如果只是测试,在本机hosts文件中也要配置域名和ip的对应关系。否则从返回的结果中也是无法访问的

当在浏览器中输入image.test.com域名时,首先会从本机hosts文件查看是否有对应的ip,如果有则去访问对应的ip资源,然后会被nginx截获,并与nginx配置的域名ip对应关系对比,如果一致则可以访问资源。

server {
    listen 80;
    autoindex on;
    server_name image.test.com;
    access_log /etc/nginx/logs/access.log combined;
    index index.html index.htm index.jsp index.php;
    #error_page 404 /404.html;
    if ( $query_string ~* ".*[\;'\<\>].*" ){
        return 404;
    }

    location ~ /(mmall_fe|mmall_admin_fe)/dist/view/* {
        deny all;
    }

    location / {
        root /ftpfile;
        add_header Access-Control-Allow-Origin *;
    }
}

  • 最后的返回结果样式, 直接访问url后的结果
{
    "status": 0,
    "data": {
        "uri": "05c0a49a-a461-4ab2-b277-58a1051f828c.jpeg",
        "url": "http://image.test.com/video/05c0a49a-a461-4ab2-b277-58a1051f828c.jpeg"
    }
}
//Linux服务器中ftpfile文件夹中的video文件下的上传文件
[slf@www ftpfile]$ cd video
[slf@www video]$ ls
05c0a49a-a461-4ab2-b277-58a1051f828c.jpeg
[slf@www video]$ 

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,820评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,648评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,324评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,714评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,724评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,328评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,897评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,804评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,345评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,431评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,561评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,238评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,928评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,417评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,528评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,983评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,573评论 2 359

推荐阅读更多精彩内容