FileFtpUploadUtil.java 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. package com.sapit.common.util;
  2. import org.apache.commons.net.ftp.FTP;
  3. import org.apache.commons.net.ftp.FTPClient;
  4. import org.apache.commons.net.ftp.FTPReply;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.beans.factory.annotation.Value;
  8. import org.springframework.stereotype.Component;
  9. import javax.imageio.ImageIO;
  10. import javax.servlet.http.HttpServletRequest;
  11. import java.awt.image.BufferedImage;
  12. import java.io.ByteArrayInputStream;
  13. import java.io.ByteArrayOutputStream;
  14. import java.io.IOException;
  15. import java.io.InputStream;
  16. /**
  17. * @version 1.0
  18. * @description: 上传文件到ftp服务器
  19. * @author: sap1
  20. * @date: 2023/2/16 10:21
  21. */
  22. @Component
  23. public class FileFtpUploadUtil {
  24. // 日志输出
  25. static final Logger log = LoggerFactory.getLogger(FileFtpUploadUtil.class);
  26. private static String uploadIp = "192.168.200.21";
  27. private static String uploadPort = "21";
  28. private static String uploadUsername = "vsftpd";
  29. private static String uploadPassword = "Q1w2e3r4Hb#";
  30. private static String savePath = "/eduepos/v1";
  31. public static String rootURL = "";
  32. @Value("${upload.savepath:#{null}}")
  33. public void setSavePath(String savePath) {
  34. FileFtpUploadUtil.savePath = savePath;
  35. }
  36. @Value("${upload.rootUrl:#{null}}")
  37. public void setRootUrl(String rootURL) {
  38. FileFtpUploadUtil.rootURL = rootURL;
  39. }
  40. @Value("${upload.ip:#{null}}")
  41. public void setUploadIp(String uploadIp) {
  42. FileFtpUploadUtil.uploadIp = uploadIp;
  43. }
  44. @Value("${upload.port:#{null}}")
  45. public void setUploadPort(String uploadPort) {
  46. FileFtpUploadUtil.uploadPort = uploadPort;
  47. }
  48. @Value("${upload.username:#{null}}")
  49. public void setUploadUsername(String uploadUsername) {
  50. FileFtpUploadUtil.uploadUsername = uploadUsername;
  51. }
  52. @Value("${upload.password:#{null}}")
  53. public void setUploadPassword(String uploadPassword) {
  54. FileFtpUploadUtil.uploadPassword = uploadPassword;
  55. }
  56. private static FTPClient connection(){
  57. FTPClient ftp = new FTPClient();
  58. try {
  59. String hostname = FileFtpUploadUtil.uploadIp;
  60. int port = Integer.parseInt(FileFtpUploadUtil.uploadPort);
  61. String username = FileFtpUploadUtil.uploadUsername;
  62. String password = uploadPassword;
  63. //连接FTP服务器
  64. ftp.connect(hostname, port);
  65. ftp.setControlEncoding("UTF-8");
  66. //外网ip上传主动模式:
  67. //ftpClient.enterLocalActiveMode();
  68. //内网ip上传被动模式:
  69. ftp.enterLocalPassiveMode(); //被动模式
  70. ftp.setFileType(FTP.BINARY_FILE_TYPE);
  71. //登录ftp
  72. ftp.login(username, password);
  73. if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
  74. ftp.disconnect();
  75. }
  76. } catch (Exception e) {
  77. log.error(e.getMessage());
  78. }
  79. return ftp;
  80. }
  81. /**
  82. * 逐级进入并确保目录存在(不存在则创建),全部成功返回 true。
  83. */
  84. private static boolean ensureDir(FTPClient ftp, String dir) throws IOException {
  85. if (dir == null || dir.trim().isEmpty()) {
  86. return true;
  87. }
  88. String[] parts = dir.split("/");
  89. StringBuilder cur = new StringBuilder();
  90. for (String p : parts) {
  91. if (p == null || p.trim().isEmpty()) {
  92. continue;
  93. }
  94. cur.append("/").append(p);
  95. if (!ftp.changeWorkingDirectory(cur.toString())) {
  96. ftp.makeDirectory(cur.toString());
  97. if (!ftp.changeWorkingDirectory(cur.toString())) {
  98. return false;
  99. }
  100. }
  101. }
  102. return true;
  103. }
  104. /**
  105. * 方法名称:文件上传
  106. * 功能描述:文件上传
  107. *
  108. * 作 者:Vic
  109. * 日 期:2023-03-02
  110. */
  111. public static boolean uploadFile(String path, String fileName, InputStream input) {
  112. boolean rtn = false;
  113. FTPClient ftpClient = connection();
  114. try {
  115. int reply = ftpClient.getReplyCode();
  116. if (!FTPReply.isPositiveCompletion(reply)) {
  117. ftpClient.disconnect();
  118. return rtn;
  119. }
  120. ftpClient.setControlEncoding("UTF-8"); // 中文支持
  121. ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
  122. ftpClient.enterLocalPassiveMode();
  123. // 进入/创建目标目录,校验成功后再上传,避免目录不存在时文件被写到默认位置导致URL 404
  124. if (!ensureDir(ftpClient, savePath + path)) {
  125. ftpClient.logout();
  126. return false;
  127. }
  128. if (ftpClient.storeFile(fileName, input)) {
  129. rtn = true;
  130. }
  131. input.close();
  132. ftpClient.logout();
  133. } catch (IOException e) {
  134. e.printStackTrace();
  135. } finally {
  136. if (ftpClient.isConnected()) {
  137. try {
  138. ftpClient.disconnect();
  139. } catch (IOException e) {
  140. e.printStackTrace();
  141. }
  142. }
  143. }
  144. return rtn;
  145. }
  146. /** * 删除文件 *
  147. * @param pathname FTP服务器保存目录
  148. * @param filename 要删除的文件名称
  149. * @return
  150. * @throws IOException */
  151. public static boolean deleteFile(String filePath, String filename) {
  152. FTPClient ftpClient = connection();
  153. boolean flag = false;
  154. try {
  155. ftpClient.changeWorkingDirectory(savePath + filePath);
  156. ftpClient.dele(filename);
  157. flag = true;
  158. } catch (Exception e) {
  159. e.printStackTrace();
  160. } finally {
  161. if (ftpClient.isConnected()) {
  162. try {
  163. ftpClient.disconnect();
  164. } catch (IOException ioe) {
  165. ioe.printStackTrace();
  166. }
  167. }
  168. }
  169. return flag;
  170. }
  171. /**
  172. * 通过文件头(魔数)判断是否为常见图片格式,防止上传非图片文件。
  173. * @param in 文件输入流(调用方需自行关闭)
  174. * @return true=是图片
  175. */
  176. public static boolean isImage(InputStream in) {
  177. if (in == null) {
  178. return false;
  179. }
  180. byte[] b = new byte[12];
  181. int n;
  182. try {
  183. n = in.read(b);
  184. } catch (IOException e) {
  185. return false;
  186. }
  187. if (n < 4) {
  188. return false;
  189. }
  190. // JPEG: FF D8 FF
  191. if (n >= 3 && (b[0] & 0xFF) == 0xFF && (b[1] & 0xFF) == 0xD8 && (b[2] & 0xFF) == 0xFF) {
  192. return true;
  193. }
  194. // PNG: 89 50 4E 47
  195. if (n >= 8 && (b[0] & 0xFF) == 0x89 && b[1] == 'P' && b[2] == 'N' && b[3] == 'G') {
  196. return true;
  197. }
  198. // GIF: 47 49 46
  199. if (n >= 4 && b[0] == 'G' && b[1] == 'I' && b[2] == 'F') {
  200. return true;
  201. }
  202. // BMP: 42 4D
  203. if (n >= 2 && b[0] == 'B' && b[1] == 'M') {
  204. return true;
  205. }
  206. // WEBP: RIFF....WEBP
  207. if (n >= 12 && b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F'
  208. && b[8] == 'W' && b[9] == 'E' && b[10] == 'B' && b[11] == 'P') {
  209. return true;
  210. }
  211. return false;
  212. }
  213. /**
  214. * 组装图片对外访问URL。
  215. * rootURL 已配置时使用配置;未配置时返回相对路径(dir + fileName),由前端按自身站点解析,
  216. * 避免臆造一个可能无法访问的后端绝对地址导致图片加载失败。
  217. * @param request 当前请求(预留,暂不使用)
  218. * @param path 相对目录,如 /inspect、/rectification
  219. * @param fileName 文件名
  220. * @return 完整URL或相对路径
  221. */
  222. public static String buildRemoteUrl(HttpServletRequest request, String path, String fileName) {
  223. String root = rootURL;
  224. if (root == null) {
  225. root = "";
  226. }
  227. root = root.trim();
  228. String dir = (path == null || path.isEmpty()) ? "" : path;
  229. return root + dir + "/" + fileName;
  230. }
  231. // public static void main(String [] args) throws FileNotFoundException {
  232. // uploadIp = "123.57.45.174";
  233. // uploadPort = "21";
  234. // uploadUsername = "vsftpd";
  235. // uploadPassword = "Q1w2e3r4Hb#";
  236. // savePath = "/eduepos/v1";
  237. //
  238. //
  239. // File file = new File("D://temp/abc.png");
  240. // InputStream in = new FileInputStream(file);
  241. // ftp.enterLocalPassiveMode();
  242. // ftp.setFileType(FTP.BINARY_FILE_TYPE);
  243. // InputStream inputStream = ftp.retrieveFileStream(new String(filePath.getBytes("UTF-8"), "ISO-8859-1"));
  244. // //MultipartFile mfile = file
  245. // //FileUploadUtil.uploadFile("/aaa/bbb", "bbb.txt", in);
  246. // FileFtpUploadUtil.uploadFile("/goods", "abc.png", in);
  247. // }
  248. /**
  249. * 将BufferedImage转换为InputStream
  250. * @param image
  251. * @return
  252. */
  253. public static InputStream bufferedImageToInputStream(BufferedImage image){
  254. ByteArrayOutputStream os = new ByteArrayOutputStream();
  255. try {
  256. ImageIO.write(image, "jpg", os);
  257. InputStream input = new ByteArrayInputStream(os.toByteArray());
  258. return input;
  259. } catch (IOException e) {
  260. e.printStackTrace();
  261. }
  262. return null;
  263. }
  264. /**
  265. * 将BufferedImage转换为InputStream
  266. * @param image
  267. * @return
  268. */
  269. public static InputStream bufferedImageToInputStreamForPng(BufferedImage image){
  270. ByteArrayOutputStream os = new ByteArrayOutputStream();
  271. try {
  272. ImageIO.write(image, "png", os);
  273. InputStream input = new ByteArrayInputStream(os.toByteArray());
  274. return input;
  275. } catch (IOException e) {
  276. e.printStackTrace();
  277. }
  278. return null;
  279. }
  280. }