FTP,即:文件傳輸協議(File Transfer Protocol),基於客戶端/服務器模式,默認使用20、21端口號,其
中端口20(數據端口)用於進行數據傳輸,端口21(命令端口)用於接受客戶端發出的相關FTP命令與參
數。FTP服務器普遍部署於局域網中,具有容易搭建、方便管理的特點。而且有些FTP客戶端工具還可以支
持文件的多點下載以及斷點續傳技術,因此FTP服務得到了廣大用戶的青睞。
FTP協議有以下兩種工作模式:
主動模式(PORT):FTP服務器主動向客戶端發起連接請求。
被動模式(PASV):FTP服務器等待客戶端發起連接請求(FTP的默認工作模式)。
vsftpd是一款運行在Linux操作系統上的FTP服務程序,具有很高的安全性和傳輸速度。
vsftpd有以下三種認證模式:
匿名開放模式:是一種最不安全的認證模式,任何人都可以無需密碼驗證而直接登陸。
本地用戶模式:是通過Linux系統本地的賬戶密碼信息進行認證的模式,相較於匿名開放模式更安全,而且
配置起來簡單。
虛擬用戶模式:是這三種模式中最安全的一種認證模式,它需要為FTP服務單獨建立用戶數據庫文件,虛
擬出用來進行口令驗證的賬戶信息,而這些賬戶信息在服務器系統中實際上是不存在的,僅供FTP服務程
序進行認證使用。
下面以虛擬用戶模式配置講解
1.輸入圖中命令,若輸出圖中信息表示已安裝vsftpd,否則輸入命令:yum install -y vsftpd 進行安裝
2.安裝完成后
vsftpd缺省安裝在/etc/vsftpd/目錄中,進入該目錄后如下所示
1.vsftpd.conf:為核心配置文件
2.user_list:指定允許使用vsftpd 的用戶列表文件,即白名單
3.ftpusers:指定哪些用戶不能訪問FTP服務器,即黑名單
4.如需去掉配置文件里的注釋行輸入以下命令即可
mv /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.bak
grep -v "#" /etc/vsftpd/vsftpd.conf.bak > /etc/vsftpd/vsftpd.conf
5.vsftpd.conf常用配置參數講解
listen=<YES/NO> # YES: 服務以獨立運行方式運行; NO: 運行在 xinetd 內。 默認為 YES
listen_address=<ip address> # 服務監聽地址, 如果有多個網卡, 需要將服務綁定到指定 IP 地址
listen_port=<port> # 服務監聽端口, 默認為 21
anonymous_enable=<YES/NO> # 是否允許匿名用戶訪問, 默認 NO
anon_mkdir_write_enable=<YES/NO> # 是否允許匿名用戶創建文件夾, 默認 NO
anon_other_write_enable=<YES/NO> # 是否允許匿名用戶其他的寫權限, 創建文件、重命名、刪除文件等權限 默認為 NO
anon_upload_enable=<YES/NO> # 是否允許匿名用戶上傳, 默認 NO
anon_umask=<nnn> # 匿名用戶上傳的文件的生成掩碼, 默認為077
anon_max_rate=<n> # 匿名用戶的最大傳輸速率, 單位為 Byte/s, 值為 0 表示不限制
anon_world_readable_only=<YES/NO> # 是否允許匿名用戶只讀瀏覽
local_enable=<YES/NO> # 是否支持本地用戶帳號訪問
write_enable=<YES/NO> # 是否開放本地用戶的寫權限
local_umask=<nnn> # 本地用戶上傳的文件的生成掩碼, 默認為077
local_max_rate=<n> # 本地用戶最大的傳輸速率, 單位為 Byte/s,值為0表示不限制
local_root=<file> # 本地用戶登陸后的目錄,默認為本地用戶的主目錄
chroot_local_user=<YES/NO> # 本地用戶是否可以執行 chroot, 默認為 NO
chroot_list_enable=<YES/NO> # 是否只有指定的用戶才能執行 chroot, 默認為 NO
chroot_list_file=<filename> # 當 chroot_local_user=NO 且 chroot_list_enable=YES 時,
# 只有 filename 文件內指定的用戶(每行一個用戶名)可以執行 chroot,
# 默認值為 /etc/vsftpd.chroot_list
userlist_enable=<YES/NO> # 是否啟用 userlist_file 白/黑名單用戶列表, 默認為 NO
userlist_deny=<YES/NO> # 當 userlist_enable=YES(即啟用 userlist_file )時, 則該字段才有效。
userlist_deny=YES: userlist_file 為 黑名單, 即在該文件內的用戶均不可登錄, 其他用戶可以登錄
userlist_deny=NO: userlist_file 為 白名單, 即在該文件內的用戶才可以登錄, 其他用戶均不可登錄
userlist_file=<filename> # 黑/白名單用戶列表文件(每行一個用戶名)
# 是黑名單還是白名單, 根據 userlist_deny 的值決定
# 默認值為 /etc/vsftpd.user_list
ftpd_banner=<message> # 客戶端連接服務器后顯示的歡迎信息
connect_timeout=<n> # 遠程客戶端響應端口數據連接超時時間, 單位為秒, 默認 60
accept_connection_timeout=<n> # 空閑的數據連接超時時間, 單位為秒, 默認 120
data_connection_timeout=<n> # 空閑的用戶會話超時時間, 單位為秒, 默認 300
max_clients=<n> # 在獨立模式運行時, 最大連接數, 0 表示無限制
max_per_ip=<n> # 在獨立模式運行時, 每 IP 的最大連接數, 0表示無限制
pasv_min_port=45000 # PASV模式最小端口
pasv_max_port=49999 # PASV模式最大端口
3.配置防火牆開放vsftpd命令端口與PASV模式下的端口
firewall-cmd --zone=public --add-port=21/tcp --permanent
firewall-cmd --zone=public --add-port=45000-49000/tcp --permanent
firewall-cmd --reload
4.創建用於FTP認證的用戶數據庫文件
vim /etc/vsftpd/vuser.txt
其中奇數行為用戶名,偶數行為密碼
如:
xuyuanyuan
123456
chendanting
123456
明文信息不安全,需要使用db_load命令用哈希(hash)算法將明文信息轉換成數據文件,然后將明文信息文件刪除
db_load -T -t hash -f /etc/vsftpd/vuser.txt /etc/vsftpd/vuser.db
chmod 600 /etc/vsftpd/vuser.db
rm -f /etc/vsftpd/vuser.txt
5.創建虛擬用戶映射的系統本地用戶和FTP根目錄
輸入命令:
useradd -d /ftp_data -s /sbin/nologin virtual
chmod -Rf 755 /ftp_data
6.建立用於支持虛擬用戶的PAM文件
PAM(可插拔認證模塊)是一種認證機制,通過一些動態鏈接庫和統一的API把系統提供的服務與認證方式分
開,使得系統管理員可以根據需求靈活調整服務程序的不同認證方式。PAM采用了分層設計(應用程序層、
應用接口層、鑒別模塊層)的思想,其結構如下圖所示。
vim /etc/pam.d/vsftpd.vu
添加以下信息
auth required pam_userdb.so db=/etc/vsftpd/vuser
account required pam_userdb.so db=/etc/vsftpd/vuser
6.為vuser.txt里的用戶配置相關參數
(1) 創建存放用戶的目錄
mkdir /etc/vsftpd/vusers_dir
(2) 為各用戶創建文件,文件名即為用戶名
vim /etc/vsftpd/vusers_dir/xuyuanyuan
vim /etc/vsftpd/vusers_dir/chendanting
(3) 按各用戶需要加入參數配置
local_root=/ftp_data/xuyuanyuan
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES
anon_world_readable_only=YES
6.修改核心配置文件vsftpd.conf為以下內容
anonymous_enable=NO
local_enable=YES
# 開啟虛擬用戶模式
guest_enable=YES
# 指定虛擬用戶對應的系統用戶
guest_username=virtual
# 允許對ftp根目錄執行寫入操作
allow_writeable_chroot=YES
write_enable=YES
local_umask=022
dirmessage_enable=YES
xferlog_enable=YES
connect_from_port_20=YES
xferlog_std_format=YES
listen_port=21
listen=NO
listen_ipv6=YES
#PAM文件
pam_service_name=vsftpd.vu
userlist_enable=YES
tcp_wrappers=YES
# 虛擬用戶配置文件目錄
user_config_dir=/etc/vsftpd/vuser_dir
# 被動模式端口范圍
pasv_min_port=45000
pasv_max_port=49000
chroot_local_user=YES
7.最后啟動服務即可訪問
systemctl start vsftpd.serivce 啟動服務
systemctl stop vsftpd.serivce 停止服務
systemctl status vsftpd.serivce 查看服務狀態
systemctl restart vsftpd.serivce 查看服務狀態
在瀏覽器輸入 ftp://ip:21 就可訪問了
最后配上操作ftp的工具類
maven依賴
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>

1 /** 2 * @author Medusa 3 * @Date 12-14-19 4 * @Description ftp工具類 5 */ 6 public class FtpUtil { 7 8 /** 9 * ftp服務器的ip 10 */ 11 private String ip; 12 13 /** 14 * ftp服務器的端口,缺省為21 15 */ 16 private int port; 17 18 /** 19 * 連接ftp服務器的用戶名 20 */ 21 private String userName; 22 23 /** 24 * 用戶名對應通行證 25 */ 26 private String password; 27 28 /** 29 * 客戶端編碼 30 */ 31 private String clientCharset = "GBK"; 32 33 /** 34 * 服務端編碼,缺省為ISO8859-1 35 */ 36 private String serverCharset = "ISO8859-1"; 37 38 private FTPClient ftpClient; 39 40 public FtpUtil(String ip, int port, String userName, String password) { 41 this.ip = ip; 42 this.port = port; 43 this.userName = userName; 44 this.password = password; 45 } 46 47 /** 48 * 建立連接並登陸服務器 49 */ 50 public boolean connectFtp() { 51 ftpClient = new FTPClient(); 52 53 try { 54 ftpClient.connect(ip, port); 55 56 // 是否連接成功 57 if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { 58 System.out.println("create connect fail"); 59 ftpClient.disconnect(); 60 return false; 61 } 62 63 if (!ftpClient.login(userName, password)) { 64 System.out.println("login server fail"); 65 ftpClient.disconnect(); 66 return false; 67 } 68 69 // 開啟服務器對UTF-8的支持,如果服務器支持就用UTF-8編碼,否則使用本地編碼 70 if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) { 71 clientCharset = "UTF-8"; 72 } 73 ftpClient.setControlEncoding(clientCharset); 74 ftpClient.enterLocalPassiveMode(); // 設置被動模式 75 ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 設置文件傳輸類型 76 } catch (IOException e) { 77 System.out.println("create connect or close connect error"); 78 return false; 79 } 80 return true; 81 } 82 83 /** 84 * 退出登陸並關閉連接 85 */ 86 public boolean closeFtp() { 87 if (ftpClient != null && ftpClient.isConnected()) { 88 try { 89 ftpClient.logout(); 90 ftpClient.disconnect(); 91 return true; 92 } catch (IOException e) { 93 System.out.println("logout or close connect error"); 94 return false; 95 } 96 } 97 return false; 98 } 99 100 /** 101 * 客戶端轉服務端編碼 102 */ 103 private String clientCharsetToServer(String path) { 104 try { 105 return new String(path.getBytes(clientCharset), serverCharset); 106 } catch (UnsupportedEncodingException e) { 107 System.out.println("client to server encode error"); 108 return null; 109 } 110 } 111 112 /** 113 * 服務端轉客戶端編碼 114 */ 115 private String serverCharsetToClient(String path) { 116 try { 117 return new String(path.getBytes(serverCharset), clientCharset); 118 } catch (UnsupportedEncodingException e) { 119 System.out.println("server to client encode error"); 120 return null; 121 } 122 } 123 124 /** 125 * 獲取當前工作目錄 126 */ 127 private String getWorkDirectory() { 128 try { 129 return serverCharsetToClient(ftpClient.printWorkingDirectory()); 130 } catch (IOException e) { 131 System.out.println("get work directory error"); 132 return null; 133 } 134 } 135 136 /** 137 * 獲取當前工作目錄下的所有文件名 138 */ 139 private String[] getWorkDirectoryFileNames() { 140 try { 141 FTPFile[] ftpFiles = ftpClient.listFiles(); 142 if (ftpFiles.length > 0) { 143 String[] fileNames = new String[ftpFiles.length]; 144 int i = 0; 145 146 for (FTPFile ftpFile: ftpFiles) fileNames[i++] = ftpFile.getName(); 147 148 return fileNames; 149 } 150 return null; 151 } catch (IOException e) { 152 System.out.println("get work directory files error"); 153 return null; 154 } 155 } 156 157 /** 158 * 根據從根目錄開始的完整文件路徑,切換工作目錄 159 */ 160 private boolean changeWorkDirectory(String path) { 161 try { 162 return ftpClient.changeWorkingDirectory(clientCharsetToServer(path)); 163 } catch (IOException e) { 164 System.out.println("change directory error"); 165 return false; 166 } 167 } 168 169 /** 170 * 當前工作目錄下是否有查詢的文件 171 */ 172 private boolean isWorkDirectoryHasFile(String fileName) { 173 try { 174 FTPFile[] ftpFiles = ftpClient.listFiles(); 175 176 for (FTPFile ftpFile : ftpFiles) { 177 if (ftpFile.getName().equals(fileName)) return true; 178 } 179 180 return false; 181 } catch (IOException e) { 182 System.out.println("get work directory files error"); 183 return false; 184 } 185 } 186 187 /** 188 *獲取對應路徑下的文件(根目錄除外),並切換工作目錄至此文件的父目錄 189 */ 190 private FTPFile getFTPFile(String path) { 191 if (path.equals("/")) { 192 System.out.println("invalid root directory"); 193 return null; 194 } 195 196 String fileName = path; 197 if (!path.equals("/") && path.indexOf("/") != -1) { 198 String[] split = path.split("/"); 199 fileName = split[split.length - 1]; 200 } 201 if (changeWorkDirectory(path.substring(0, path.length() - fileName.length()))) { 202 try { 203 for (FTPFile ftpFile : ftpClient.listFiles()) { 204 if (ftpFile.getName().equals(fileName)) return ftpFile; 205 } 206 } catch (IOException e) { 207 System.out.println("get work directory files error"); 208 return null; 209 } 210 } 211 return null; 212 } 213 214 /** 215 * 獲取服務端文件夾下所有文件路徑 216 */ 217 public List<List<String>> getDirectoryUnderAllFile(String path) throws IOException { 218 // 存放目錄和文件的路徑 219 List<List<String>> directoryAndFilePaths = new ArrayList<>(); 220 // 存放目錄的路徑 221 List<String> directoryPaths = new ArrayList<>(); 222 // 存放文件的路徑 223 List<String> filePaths = new ArrayList<>(); 224 // 待遍歷處理的文件夾路徑 225 Queue<String> ergodicDirPaths = new LinkedList<>(); 226 ergodicDirPaths.offer(path); 227 directoryPaths.add(path); 228 229 while (ergodicDirPaths.size() > 0) { 230 String entry = ergodicDirPaths.poll(); 231 if (changeWorkDirectory(entry)) { 232 entry = "/".endsWith(entry) ? "" : entry; 233 FTPFile[] ftpFiles = ftpClient.listFiles(); 234 for (FTPFile ftpFile : ftpFiles) { 235 if (ftpFile.isFile()) { 236 filePaths.add(entry + "/" + ftpFile.getName()); 237 } else { 238 directoryPaths.add(entry + "/" + ftpFile.getName()); 239 ergodicDirPaths.offer(entry + "/" + ftpFile.getName()); 240 } 241 } 242 } else { 243 System.out.println("切換目錄失敗"); 244 return null; 245 } 246 } 247 248 directoryAndFilePaths.add(directoryPaths); 249 directoryAndFilePaths.add(filePaths); 250 return directoryAndFilePaths; 251 } 252 253 /** 254 * 獲取客戶端文件夾下所有文件路徑 255 */ 256 public List<List<String>> getDirectoryUnderAllFile1(String path) { 257 // 存放目錄和文件的路徑 258 List<List<String>> directoryAndFilePaths = new ArrayList<>(); 259 // 存放目錄的路徑 260 List<String> directoryPaths = new ArrayList<>(); 261 // 存放文件的路徑 262 List<String> filePaths = new ArrayList<>(); 263 // 待遍歷處理的文件夾路徑 264 Queue<String> ergodicDirPaths = new LinkedList<>(); 265 ergodicDirPaths.offer(path); 266 directoryPaths.add(path); 267 268 while (ergodicDirPaths.size() > 0) { 269 String entry = ergodicDirPaths.poll(); 270 271 File dir = new File(entry); 272 for (File file : dir.listFiles()) { 273 if (file.isFile()) { 274 filePaths.add(entry + "/" + file.getName()); 275 } else { 276 directoryPaths.add(entry + "/" + file.getName()); 277 ergodicDirPaths.offer(entry + "/" + file.getName()); 278 } 279 } 280 } 281 282 directoryAndFilePaths.add(directoryPaths); 283 directoryAndFilePaths.add(filePaths); 284 return directoryAndFilePaths; 285 } 286 287 /** 288 * 下載文件,若客戶端已存在則覆蓋它 289 */ 290 public boolean download(String serverPath, String clientPath) { 291 // 判斷服務端文件是否存在 292 FTPFile ftpFile = getFTPFile(serverPath); 293 if (ftpFile == null) { 294 System.out.println("server file not exist"); 295 return false; 296 } 297 298 // 若客戶端目錄不存在,就創建它,否則清空目錄 299 File clientDirectory = new File(clientPath); 300 if (!clientDirectory.exists()) { 301 if (!clientDirectory.mkdirs()) { 302 System.out.println("client create directory fail"); 303 return false; 304 } 305 } else { 306 if (!clientDirectory.isDirectory()) { 307 System.out.println("clientPath is not directory"); 308 return false; 309 } else { 310 if (!emptyDirectory(clientDirectory)) { 311 System.out.println("empty client directory fail"); 312 return false; 313 } 314 } 315 } 316 317 if (ftpFile.isFile()) { 318 executeDownload(serverPath, clientPath + serverPath.substring(serverPath.lastIndexOf("/"))); 319 } else { 320 try { 321 List<List<String>> allFile = getDirectoryUnderAllFile(serverPath); 322 // 獲取所有目錄路徑 323 List<String> directoryPaths = allFile.get(0); 324 if (directoryPaths.size() > 0) { 325 directoryPaths.forEach(path -> new File(clientPath + path).mkdir()); 326 } else { 327 System.out.println("get all directory fail"); 328 return false; 329 } 330 331 // 獲取所有文件路徑 332 List<String> filePaths = allFile.get(1); 333 for (String path : filePaths) { 334 executeDownload(path, clientPath + path); 335 } 336 } catch (IOException e) { 337 System.out.println("error getting all files under this path"); 338 return false; 339 } 340 } 341 return true; 342 } 343 344 /** 345 * 執行下載文件 346 */ 347 private void executeDownload(String serverPath, String clientPath) { 348 InputStream is = null; 349 OutputStream os = null; 350 try { 351 is = ftpClient.retrieveFileStream(clientCharsetToServer(serverPath)); 352 File file = new File(clientPath); 353 if (!file.createNewFile()) { 354 System.out.println("client create file fail"); 355 return; 356 } 357 os = new FileOutputStream(file); 358 byte[] by = new byte[1024]; 359 int len = 0; 360 while ((len = is.read(by)) != -1) os.write(by, 0, len); 361 } catch (IOException e) { 362 System.out.println("stream handle file fail"); 363 return; 364 } finally { 365 try { 366 if (is != null) { 367 is.close(); 368 // 必須調用此方法,否則第二次調用retrieveFileStream()方法時,返回null 369 ftpClient.completePendingCommand(); 370 } 371 if (os != null) os.close(); 372 } catch (IOException e) { 373 e.printStackTrace(); 374 } 375 } 376 } 377 378 /** 379 * 執行上傳文件 380 */ 381 private void executeUpload(String serverPath, String clientPath) { 382 InputStream is = null; 383 OutputStream os = null; 384 try { 385 is = new FileInputStream(clientPath); 386 os = ftpClient.storeFileStream(clientCharsetToServer(serverPath)); 387 388 byte[] by = new byte[1024]; 389 int len = 0; 390 while ((len = is.read(by)) != -1) os.write(by, 0, len); 391 } catch (FileNotFoundException e) { 392 System.out.println("client file not found"); 393 return; 394 } catch (IOException e) { 395 System.out.println("stream handle file fail"); 396 return; 397 } finally { 398 try { 399 if (is != null) is.close(); 400 if (os != null) { 401 os.close(); 402 ftpClient.completePendingCommand(); 403 } 404 } catch (IOException e) { 405 e.printStackTrace(); 406 } 407 } 408 } 409 410 /** 411 * 上傳文件,若服務端已存在則覆蓋它 412 */ 413 public boolean upload(String serverPath, String clientPath) { 414 File clientFile = new File(clientPath); 415 if (!clientFile.exists()) { 416 System.out.println("client file not exist"); 417 return false; 418 } 419 420 if (!"/".equals(serverPath)) { 421 FTPFile ftpFile = getFTPFile(serverPath); 422 if (ftpFile == null) { 423 String[] split = serverPath.split("/"); 424 changeWorkDirectory("/"); 425 try { 426 for (int i = 1; i < split.length; i++) { 427 ftpClient.makeDirectory(clientCharsetToServer(split[i])); 428 changeWorkDirectory(getWorkDirectory() + "/" + split[i]); 429 } 430 } catch (IOException e) { 431 System.out.println("create server directory error"); 432 return false; 433 } 434 } 435 } 436 437 serverPath = "/".endsWith(serverPath) ? "" : serverPath; 438 if (clientFile.isFile()) { 439 try { 440 String filePath = serverPath + clientPath.substring(clientPath.lastIndexOf("/")); 441 ftpClient.deleteFile(filePath); 442 executeUpload(filePath, clientPath); 443 } catch (IOException e) { 444 System.out.println("delete server file error"); 445 return false; 446 } 447 } else { 448 List<List<String>> allFile = getDirectoryUnderAllFile1(clientPath); 449 List<String> directoryPaths = allFile.get(0); 450 String path2; 451 if (directoryPaths.size() > 0) { 452 try { 453 String firstDir = directoryPaths.get(0); 454 String path1 = firstDir.substring(firstDir.lastIndexOf("/")); 455 path2 = firstDir.substring(0, firstDir.lastIndexOf("/")); 456 ftpClient.makeDirectory(clientCharsetToServer(serverPath + path1)); 457 for (int i = 1; i < directoryPaths.size(); i++) { 458 String tempPath = directoryPaths.get(i).substring(path2.length()); 459 ftpClient.makeDirectory(clientCharsetToServer(serverPath + tempPath)); 460 } 461 } catch (IOException e) { 462 System.out.println("create server directory error"); 463 return false; 464 } 465 } else { 466 System.out.println("get all directory fail"); 467 return false; 468 } 469 List<String> filePaths = allFile.get(1); 470 for (String path : filePaths) { 471 String tempPath = path.substring(path2.length()); 472 executeUpload(serverPath + tempPath, path); 473 } 474 } 475 return true; 476 } 477 478 /** 479 * 清空目錄 480 */ 481 private boolean emptyDirectory(File dir) { 482 File[] oneLevelFile = dir.listFiles(); 483 if (oneLevelFile.length > 0) { 484 Stack<File> willDeleteFile = new Stack<>(); 485 for (int i = 0; i < oneLevelFile.length; i++) { 486 willDeleteFile.push(oneLevelFile[i]); 487 } 488 489 while (willDeleteFile.size() > 0) { 490 File entry = willDeleteFile.pop(); 491 if (entry.isFile()) { 492 if (!entry.delete()) return false; 493 } else { 494 File[] lowerLevelFile = entry.listFiles(); 495 if (lowerLevelFile.length > 0) { 496 willDeleteFile.push(entry); 497 for (int i = 0; i < lowerLevelFile.length; i++) { 498 willDeleteFile.push(lowerLevelFile[i]); 499 } 500 } else { 501 if (!entry.delete()) return false; 502 } 503 } 504 } 505 } 506 return true; 507 } 508 509 /** 510 * web端上傳 511 */ 512 public boolean webUpload(MultipartFile file, String serverPath) { 513 if (!"/".equals(serverPath)) { 514 FTPFile ftpFile = getFTPFile(serverPath); 515 if (ftpFile == null) { 516 String[] split = serverPath.split("/"); 517 changeWorkDirectory("/"); 518 try { 519 for (int i = 1; i < split.length; i++) { 520 ftpClient.makeDirectory(clientCharsetToServer(split[i])); 521 changeWorkDirectory(getWorkDirectory() + "/" + split[i]); 522 } 523 } catch (IOException e) { 524 System.out.println("create server directory error"); 525 return false; 526 } 527 } 528 } 529 String filePath; 530 try { 531 serverPath = "/".endsWith(serverPath) ? "" : serverPath; 532 filePath = serverPath + "/" + file.getOriginalFilename(); 533 ftpClient.deleteFile(filePath); 534 } catch (IOException e) { 535 System.out.println("delete server file error"); 536 return false; 537 } 538 539 InputStream is = null; 540 OutputStream os = null; 541 try { 542 is = file.getInputStream(); 543 os = ftpClient.storeFileStream(clientCharsetToServer(filePath)); 544 545 byte[] by = new byte[1024]; 546 int len = 0; 547 while ((len = is.read(by)) != -1) os.write(by, 0, len); 548 } catch (FileNotFoundException e) { 549 System.out.println("client file not found"); 550 return false; 551 } catch (IOException e) { 552 System.out.println("stream handle file fail"); 553 return false; 554 } finally { 555 try { 556 if (is != null) is.close(); 557 if (os != null) { 558 os.close(); 559 ftpClient.completePendingCommand(); 560 } 561 } catch (IOException e) { 562 e.printStackTrace(); 563 } 564 } 565 return true; 566 } 567 568 /** 569 * web端下載 570 */ 571 public boolean webDownload(HttpServletResponse response, String serverPath) { 572 FTPFile ftpFile = getFTPFile(serverPath); 573 if (ftpFile == null) { 574 System.out.println("server file not exist"); 575 return false; 576 } 577 578 InputStream is = null; 579 try { 580 is = ftpClient.retrieveFileStream(clientCharsetToServer(serverPath)); 581 ServletOutputStream sos = response.getOutputStream(); 582 byte[] by = new byte[1024]; 583 int len = 0; 584 while ((len = is.read(by)) != -1) sos.write(by, 0, len); 585 } catch (IOException e) { 586 System.out.println("stream handle file fail"); 587 return false; 588 } finally { 589 try { 590 if (is != null) { 591 is.close(); 592 ftpClient.completePendingCommand(); 593 } 594 } catch (IOException e) { 595 e.printStackTrace(); 596 } 597 } 598 599 return true; 600 } 601 602 }