天天看點

高可用的Spring FTP上傳下載下傳工具類(已解決上傳過程常見問題)

點選上方 "程式員小樂"關注, 星标或置頂一起成長

每天淩晨00點00分, 第一時間與你相約

每日英文

For something,we can't understand when we are young but by the time we understand,we are no longer young.

有些事情,當我們年輕的時候無法懂得,當我們懂得的時候已不再年輕。

每日掏心話

人無所舍,必無所成。心無所依,必無所獲。自己的路隻有自己去走,自己的心還須自己去度。

來自:宇的季節 | 責編:樂樂

連結:cnblogs.com/chenkeyu/p/8001624.html

高可用的Spring FTP上傳下載下傳工具類(已解決上傳過程常見問題)

程式員小樂(ID:study_tech)第 962 次推文  圖源:百度

往日回顧:40+ 張最全 Linux / C / C++ 思維導圖,收藏!

   正文   

前言

最近在項目中需要和ftp伺服器進行互動,在網上找了一下關于ftp上傳下載下傳的工具類,大緻有兩種。

  • 第一種是單例模式的類。
  • 第二種是另外定義一個Service,直接通過Service來實作ftp的上傳下載下傳删除。

這兩種感覺都有利弊。

  • 第一種實作了代碼複用,但是配置資訊全需要寫在類中,維護比較複雜。
  • 第二種如果是spring架構,可以通過propertis檔案,動态的注入配置資訊,但是又不能代碼複用。

是以我打算自己實作一個工具類,來把上面的兩種優點進行整合。順便把一些上傳過程中一些常見的問題也給解決了。

因為我使用的是spring架構,如果把工具類聲明為bean給spring管理,他預設就是單例的,是以不需要我再實作單例。并且因為是bean,是以我可以把properties檔案的屬性注入bean的屬性中,實作解耦,下面是具體代碼:

關注公衆号程式員小樂回複關鍵字“offer”擷取算法面試題和答案。

package com.cky.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

//使用spring自動生成單例對象,
//@Component
public class FtpUtil {
    //通過properties檔案自動注入
    @Value("${ftp.host}")
    private String host;    //ftp伺服器ip
    @Value("${ftp.port}")
    private int port;        //ftp伺服器端口
    @Value("${ftp.username}")
    private String username;//使用者名
    @Value("${ftp.password}")
    private String password;//密碼
    @Value("${ftp.basePath}")
    private String basePath;//存放檔案的基本路徑
    //測試的時候把這個構造函數打開,設定你的初始值,然後在代碼後面的main方法運作測試
    public FtpUtil() {
        //System.out.println(this.toString());
        host="192.168.100.77";
        port=21;
        username="ftpuser";
        password="ftp54321";
        basePath="/home/ftpuser/www/images";
    }
    /**
     * 
     * @param path        上傳檔案存放在伺服器的路徑
     * @param filename    上傳檔案名
     * @param input        輸入流
     * @return
     */
    public boolean fileUpload(String path,String filename,InputStream input) {
        FTPClient ftp=new FTPClient();
        try {
            ftp.connect(host, port);
            ftp.login(username, password);
            //設定檔案編碼格式
            ftp.setControlEncoding("UTF-8");
            //ftp通信有兩種模式
                //PORT(主動模式)用戶端開通一個新端口(>1024)并通過這個端口發送指令或傳輸資料,期間服務端隻使用他開通的一個端口,例如21
                //PASV(被動模式)用戶端向服務端發送一個PASV指令,服務端開啟一個新端口(>1024),并使用這個端口與用戶端的21端口傳輸資料
                //由于用戶端不可控,防火牆等原因,是以需要由服務端開啟端口,需要設定被動模式
            ftp.enterLocalPassiveMode();
            //設定傳輸方式為流方式
            ftp.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
            //擷取狀态碼,判斷是否連接配接成功
            if(!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
                throw new RuntimeException("FTP伺服器拒絕連接配接");
            }
            //轉到上傳檔案的根目錄
            if(!ftp.changeWorkingDirectory(basePath)) {
                throw new RuntimeException("根目錄不存在,需要建立");
            }
            //判斷是否存在目錄
            if(!ftp.changeWorkingDirectory(path)) {
                String[] dirs=path.split("/");
                //建立目錄
                for (String dir : dirs) {
                    if(null==dir||"".equals(dir)) continue;
                    //判斷是否存在目錄
                    if(!ftp.changeWorkingDirectory(dir)) {
                        //不存在則建立
                        if(!ftp.makeDirectory(dir)) {
                            throw new RuntimeException("子目錄建立失敗");
                        }
                        //進入新建立的目錄
                        ftp.changeWorkingDirectory(dir);
                    }
                }
                //設定上傳檔案的類型為二進制類型
                ftp.setFileType(FTP.BINARY_FILE_TYPE);
                //上傳檔案
                if(!ftp.storeFile(filename, input)) {
                    return false;
                }
                input.close();
                ftp.logout();
                return true;
            }
            
            
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            if(ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return false;
    }
    /**
     * 
     * @param filename    檔案名,注意!此處檔案名為加路徑檔案名,如:/2015/06/04/aa.jpg
     * @param localPath    存放到本地第位址
     * @return        
     */
    public boolean downloadFile(String filename,String localPath) {
        FTPClient ftp=new FTPClient();
        try {
            ftp.connect(host, port);
            ftp.login(username, password);
            //設定檔案編碼格式
            ftp.setControlEncoding("UTF-8");
            //ftp通信有兩種模式
                //PORT(主動模式)用戶端開通一個新端口(>1024)并通過這個端口發送指令或傳輸資料,期間服務端隻使用他開通的一個端口,例如21
                //PASV(被動模式)用戶端向服務端發送一個PASV指令,服務端開啟一個新端口(>1024),并使用這個端口與用戶端的21端口傳輸資料
                //由于用戶端不可控,防火牆等原因,是以需要由服務端開啟端口,需要設定被動模式
            ftp.enterLocalPassiveMode();
            //設定傳輸方式為流方式
            ftp.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
            //擷取狀态碼,判斷是否連接配接成功
            if(!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
                throw new RuntimeException("FTP伺服器拒絕連接配接");
            }
            
            int index=filename.lastIndexOf("/");
            //擷取檔案的路徑
            String path=filename.substring(0, index);
            //擷取檔案名
            String name=filename.substring(index+1);
            //判斷是否存在目錄
            if(!ftp.changeWorkingDirectory(basePath+path)) {
                throw new RuntimeException("檔案路徑不存在:"+basePath+path);
            }
            //擷取該目錄所有檔案
            FTPFile[] files=ftp.listFiles();
            for (FTPFile file : files) {
                //判斷是否有目标檔案
                //System.out.println("檔案名"+file.getName()+"---"+name);
                if(file.getName().equals(name)) {
                    //System.out.println("找到檔案");
                    //如果找到,将目标檔案複制到本地
                    File localFile =new File(localPath+"/"+file.getName());
                    OutputStream out=new FileOutputStream(localFile);
                    ftp.retrieveFile(file.getName(), out);
                    out.close();
                }
            }
            ftp.logout();
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            if(ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    
    public boolean deleteFile(String filename) {
        FTPClient ftp=new FTPClient();
        try {
            ftp.connect(host, port);
            ftp.login(username, password);
            //設定編碼格式
            ftp.setControlEncoding("UTF-8");
            ftp.enterLocalPassiveMode();
            //擷取狀态碼,判斷是否連接配接成功
            if(!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
                throw new RuntimeException("FTP伺服器拒絕連接配接");
            }
            int index=filename.lastIndexOf("/");
            //擷取檔案的路徑
            String path=filename.substring(0, index);
            //擷取檔案名
            String name=filename.substring(index+1);
            //判斷是否存在目錄,不存在則說明檔案存在
            if(!ftp.changeWorkingDirectory(basePath+path)) {
                return true;
            }
            if(ftp.deleteFile(name)) {
                clearDirectory(ftp, basePath, path);
                ftp.logout();
                return true;
            }
            ftp.logout();
            return false;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            if(ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    
    /**
     * 
     * @param ftp    
     * @param basePath    
     * @param path        以path為根,遞歸清除上面所有空的檔案夾,直到出現不為空的檔案夾停止,最多清除到basePath結束
     * @throws IOException
     */
    private void clearDirectory(FTPClient ftp,String basePath,String path) throws IOException {
        //如果路徑長度小于2,說明到頂了
        if(path.length()<2) {
            return ;
        }
        //如果目前目錄檔案數目小于1則删除此目錄
        if(ftp.listNames(basePath+path).length<1) {
            //删除目錄
            System.out.println("删除目錄:"+basePath+path);
            ftp.removeDirectory(basePath+path);
            int index=path.lastIndexOf("/");
            //路徑向上一層
            path=path.substring(0, index);
            //繼續判斷
            clearDirectory(ftp, basePath, path);
        }
    }
    
    //兩個功能其中一個使用的話另一個需要注釋
    public static void main(String []args) {
        //上傳測試--------------------------------------
        /*FileInputStream in;
        try {
            in=new FileInputStream(new File("C:\\Users\\Administrator\\Desktop\\json.png"));
            FtpUtil ftputil=new FtpUtil();
            boolean flag=ftputil.fileUpload("/2015/06/04", "va.jpg", in);
            System.out.println(flag);
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
        }*/
        //下載下傳測試--------------------------------------
        /*String filename="/2015/06/04/aa.jpg";
        String localPath="F:\\";
        FtpUtil ftputil=new FtpUtil();
        ftputil.downloadFile(filename, localPath);*/
        //删除測試--------------------------------------
        FtpUtil ftputil=new FtpUtil();
        boolean flag=ftputil.deleteFile("/2015/06/04/va.jpg");
        System.out.println(flag);
    }
    
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getBasePath() {
        return basePath;
    }
    public void setBasePath(String basePath) {
        this.basePath = basePath;
    }
    @Override
    public String toString() {
        return "FtpUtil [host=" + host + ", port=" + port + ", username=" + username + ", password=" + password
                + ", basePath=" + basePath + "]";
    }
    
}
           

具體使用

第一步:配置spring加載properties檔案

關注公衆号程式員小樂回複關鍵字“Java”擷取Java面試題和答案。

applicationContext.xml

<context:property-placeholder location="classpath:*.properties"/>
           

ftp.properties

ftp.host=192.168.100.77
ftp.port=21
ftp.username=ftpuser
ftp.password=ftp54321
ftp.basePath=/home/ftpuser/
           

第二步:将工具類聲明為bean

xml方式

<bean id="ftpUtil" class="com.cky.util.FtpUtil">
        <property name="host" value="${ftp.host}"></property>
        <property name="port" value="${ftp.port}"></property>
        <property name="username" value="${ftp.username}"></property>
        <property name="password" value="${ftp.password}"></property>
        <property name="basePath" value="${ftp.basePath}"></property>
</bean>
           

注解方式,元件掃描

<context:component-scan base-package="com.cky.util"></context:component-scan>
           

第三步:注入使用

@Autowired
    private FtpUtil ftpUtil;
           

FTP常見問題:

1、連接配接失敗

(1)檢查ftp伺服器是否打開

(2)檢查防火牆是否将ftp伺服器端口加入白名單(測試的話也可以直接把防火牆關掉)

2、建立檔案或者上傳檔案失敗:

最常見的就是檔案權限問題造成的,不光是讀寫權限,還有檔案的使用者組。

例如:ftpClient登入的使用者是ftpUser   一般是檔案存放在/home/ftpUser中,一般我們不會直接放在根目錄,而是建立對應檔案夾,這時需要注意,建立檔案夾時需要以ftpUser或者同一組使用者的身份建立,如果你是以root使用者建立的,那麼ftpUser将無權通路!

高可用的Spring FTP上傳下載下傳工具類(已解決上傳過程常見問題)

歡迎在留言區留下你的觀點,一起讨論提高。如果今天的文章讓你有新的啟發,歡迎轉發分享給更多人。歡迎加入程式員小樂加群”或者“阿裡、騰訊、百度、華為、京東最新面試題彙集

如何保證緩存與資料庫的雙寫一緻性?

分布式之分布式事務、分布式鎖、分布式Session

用 GitLab 做 CI/CD 是什麼感覺,太強了!!

高可用的Spring FTP上傳下載下傳工具類(已解決上傳過程常見問題)
關注訂閱号「程式員小樂」,收看更多精彩内容
           

嘿,你在看嗎?

高可用的Spring FTP上傳下載下傳工具類(已解決上傳過程常見問題)