FileUploadUtils.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. package com.ruoyi.common.utils.file;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.net.URLEncoder;
  6. import java.nio.file.Paths;
  7. import java.util.*;
  8. import cn.hutool.http.HttpRequest;
  9. import cn.hutool.http.HttpResponse;
  10. import cn.hutool.json.JSONString;
  11. import cn.hutool.json.JSONUtil;
  12. import com.alibaba.fastjson2.JSON;
  13. import com.alibaba.fastjson2.JSONArray;
  14. import com.alibaba.fastjson2.JSONObject;
  15. import com.beust.jcommander.internal.Maps;
  16. import com.ruoyi.common.core.domain.AjaxResult;
  17. import com.ruoyi.common.utils.Base64Util;
  18. import com.ruoyi.common.utils.HttpUtils;
  19. import okhttp3.*;
  20. import org.apache.commons.io.FilenameUtils;
  21. import org.apache.pdfbox.pdmodel.PDDocument;
  22. import org.apache.pdfbox.pdmodel.PDPage;
  23. import org.apache.pdfbox.pdmodel.PDPageContentStream;
  24. import org.apache.pdfbox.pdmodel.common.PDRectangle;
  25. import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
  26. import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
  27. import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
  28. import org.springframework.mock.web.MockMultipartFile;
  29. import org.springframework.web.multipart.MultipartFile;
  30. import com.ruoyi.common.config.RuoYiConfig;
  31. import com.ruoyi.common.constant.Constants;
  32. import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
  33. import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
  34. import com.ruoyi.common.exception.file.InvalidExtensionException;
  35. import com.ruoyi.common.utils.DateUtils;
  36. import com.ruoyi.common.utils.StringUtils;
  37. import com.ruoyi.common.utils.uuid.Seq;
  38. import javax.imageio.ImageIO;
  39. /**
  40. * 文件上传工具类
  41. *
  42. * @author ruoyi
  43. */
  44. public class FileUploadUtils {
  45. /**
  46. * 默认大小 50M
  47. */
  48. public static final long DEFAULT_MAX_SIZE = 500 * 1024 * 1024;
  49. /**
  50. * 默认的文件名最大长度 100
  51. */
  52. public static final int DEFAULT_FILE_NAME_LENGTH = 100;
  53. /**
  54. * 默认上传的地址
  55. */
  56. private static String defaultBaseDir = RuoYiConfig.getProfile();
  57. public static void setDefaultBaseDir(String defaultBaseDir) {
  58. FileUploadUtils.defaultBaseDir = defaultBaseDir;
  59. }
  60. public static String getDefaultBaseDir() {
  61. return defaultBaseDir;
  62. }
  63. /**
  64. * 以默认配置进行文件上传
  65. *
  66. * @param file 上传的文件
  67. * @return 文件名称
  68. * @throws Exception
  69. */
  70. public static final String upload(MultipartFile file) throws IOException {
  71. try {
  72. return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
  73. } catch (Exception e) {
  74. throw new IOException(e.getMessage(), e);
  75. }
  76. }
  77. /**
  78. * 根据文件路径上传
  79. *
  80. * @param baseDir 相对应用的基目录
  81. * @param file 上传的文件
  82. * @return 文件名称
  83. * @throws IOException
  84. */
  85. public static final String upload(String baseDir, MultipartFile file) throws IOException {
  86. try {
  87. return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
  88. } catch (Exception e) {
  89. throw new IOException(e.getMessage(), e);
  90. }
  91. }
  92. /**
  93. * 根据文件路径上传 合同编码
  94. *
  95. * @param baseDir 相对应用的基目录
  96. * @param file 上传的文件
  97. * @param loanApplicationNumber 合同编码
  98. * @return 文件名称
  99. * @throws IOException
  100. */
  101. public static final String uploadLoanApplicationNumber(String baseDir, MultipartFile file, String loanApplicationNumber, String originalFilename) throws IOException {
  102. try {
  103. return uploadLoanApplicationNumber(baseDir, file, loanApplicationNumber, originalFilename, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
  104. } catch (Exception e) {
  105. throw new IOException(e.getMessage(), e);
  106. }
  107. }
  108. /**
  109. * 文件上传
  110. *
  111. * @param baseDir 相对应用的基目录
  112. * @param file 上传的文件
  113. * @param allowedExtension 上传文件类型
  114. * @return 返回上传成功的文件名
  115. * @throws FileSizeLimitExceededException 如果超出最大大小
  116. * @throws FileNameLengthLimitExceededException 文件名太长
  117. * @throws IOException 比如读写文件出错时
  118. * @throws InvalidExtensionException 文件校验异常
  119. */
  120. public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
  121. throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
  122. InvalidExtensionException {
  123. int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
  124. if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
  125. throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
  126. }
  127. assertAllowed(file, allowedExtension);
  128. String fileName = extractFilename(file);
  129. String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
  130. file.transferTo(Paths.get(absPath));
  131. return getPathFileName(baseDir, fileName);
  132. }
  133. /**
  134. * 文件上传合同编号
  135. *
  136. * @param baseDir 相对应用的基目录
  137. * @param file 上传的文件
  138. * @param allowedExtension 上传文件类型
  139. * @param loanApplicationNumber 合同编码
  140. * @return 返回上传成功的文件名
  141. * @throws FileSizeLimitExceededException 如果超出最大大小
  142. * @throws FileNameLengthLimitExceededException 文件名太长
  143. * @throws IOException 比如读写文件出错时
  144. * @throws InvalidExtensionException 文件校验异常
  145. */
  146. public static final String uploadLoanApplicationNumber(String baseDir, MultipartFile file, String loanApplicationNumber, String originalFilename, String[] allowedExtension)
  147. throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
  148. InvalidExtensionException {
  149. int fileNamelength = Objects.requireNonNull(originalFilename).length();
  150. if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
  151. throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
  152. }
  153. assertAllowed(file, allowedExtension);
  154. String fileName = extractFilenameLoanApplicationNumber(file, loanApplicationNumber, originalFilename);
  155. String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
  156. file.transferTo(Paths.get(absPath));
  157. return getPathFileName(baseDir, fileName);
  158. }
  159. /**
  160. * 编码文件名
  161. */
  162. public static final String extractFilename(MultipartFile file) {
  163. return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
  164. FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
  165. }
  166. /**
  167. * 编码文件名根据合同编号
  168. */
  169. public static final String extractFilenameLoanApplicationNumber(MultipartFile file, String loanApplicationNumber, String originalFilename) {
  170. return StringUtils.format("{}/{}_{}.{}", loanApplicationNumber,
  171. FilenameUtils.getBaseName(originalFilename), Seq.getId(Seq.uploadSeqType), getExtension(file));
  172. }
  173. /**
  174. * 编码文件名根据合同编号
  175. */
  176. public static final String extractFilenameLoanApplicationNumberNoHzm(String originalFilename) {
  177. return StringUtils.format("{}_{}",
  178. FilenameUtils.getBaseName(originalFilename), Seq.getId(Seq.uploadSeqType));
  179. }
  180. public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
  181. File desc = new File(uploadDir + File.separator + fileName);
  182. if (!desc.exists()) {
  183. if (!desc.getParentFile().exists()) {
  184. desc.getParentFile().mkdirs();
  185. }
  186. }
  187. return desc;
  188. }
  189. public static final String getPathFileName(String uploadDir, String fileName) throws IOException {
  190. int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
  191. String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
  192. return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
  193. }
  194. /**
  195. * 文件大小校验
  196. *
  197. * @param file 上传的文件
  198. * @return
  199. * @throws FileSizeLimitExceededException 如果超出最大大小
  200. * @throws InvalidExtensionException
  201. */
  202. public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
  203. throws FileSizeLimitExceededException, InvalidExtensionException {
  204. long size = file.getSize();
  205. if (size > DEFAULT_MAX_SIZE) {
  206. throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
  207. }
  208. String fileName = file.getOriginalFilename();
  209. String extension = getExtension(file);
  210. if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
  211. if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
  212. throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
  213. fileName);
  214. } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
  215. throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
  216. fileName);
  217. } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
  218. throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
  219. fileName);
  220. } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {
  221. throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
  222. fileName);
  223. } else {
  224. throw new InvalidExtensionException(allowedExtension, extension, fileName);
  225. }
  226. }
  227. }
  228. /**
  229. * 判断MIME类型是否是允许的MIME类型
  230. *
  231. * @param extension
  232. * @param allowedExtension
  233. * @return
  234. */
  235. public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
  236. for (String str : allowedExtension) {
  237. if (str.equalsIgnoreCase(extension)) {
  238. return true;
  239. }
  240. }
  241. return false;
  242. }
  243. /**
  244. * 获取文件名的后缀
  245. *
  246. * @param file 表单文件
  247. * @return 后缀名
  248. */
  249. public static final String getExtension(MultipartFile file) {
  250. String extension = FilenameUtils.getExtension(file.getOriginalFilename());
  251. if (StringUtils.isEmpty(extension)) {
  252. extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
  253. }
  254. return extension;
  255. }
  256. public static String uploadLoanApplicationNumberOldName(String baseDir, MultipartFile file, String loanApplicationNumber, String originalFilename) throws IOException {
  257. try {
  258. return uploadLoanApplicationNumberOldName(baseDir, file, loanApplicationNumber, originalFilename, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
  259. } catch (Exception e) {
  260. throw new IOException(e.getMessage(), e);
  261. }
  262. }
  263. private static String uploadLoanApplicationNumberOldName(String baseDir, MultipartFile file, String loanApplicationNumber, String originalFilename, String[] allowedExtension)
  264. throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
  265. InvalidExtensionException {
  266. int fileNamelength = Objects.requireNonNull(originalFilename).length();
  267. if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
  268. throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
  269. }
  270. assertAllowed(file, allowedExtension);
  271. String fileName = extractFilenameLoanApplicationNumberOldName(file, loanApplicationNumber, originalFilename);
  272. String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
  273. file.transferTo(Paths.get(absPath));
  274. return getPathFileName(baseDir, fileName);
  275. }
  276. private static String extractFilenameLoanApplicationNumberOldName(MultipartFile file, String loanApplicationNumber, String originalFilename) {
  277. return StringUtils.format("{}/{}.{}", loanApplicationNumber,
  278. FilenameUtils.getBaseName(originalFilename), getExtension(file));
  279. }
  280. /**
  281. * 合并图片生成PDF保存为文件
  282. *
  283. * @return
  284. */
  285. public static AjaxResult createPdfFromImages(List<String> imagePaths, File outputFile) throws IOException {
  286. try (PDDocument document = new PDDocument()) {
  287. float margin = 50f; // Define the margin
  288. // 压缩图片质量,例如设置0.5的压缩质量
  289. float quality = 0.5f;
  290. for (String imagePath : imagePaths) {
  291. // Load image
  292. PDImageXObject image = JPEGFactory.createFromImage(document, ImageIO.read(new File(imagePath)), quality);
  293. // Calculate scaling factor to fit image within the page minus margins, maintaining aspect ratio
  294. float imgWidth = image.getWidth();
  295. float imgHeight = image.getHeight();
  296. float pageWidthForImage = PDRectangle.A4.getWidth() - 2 * margin; // Subtracting total margin from width
  297. float pageHeightForImage = PDRectangle.A4.getHeight() - 2 * margin; // Subtracting total margin from height
  298. float scaleX = pageWidthForImage / imgWidth;
  299. float scaleY = pageHeightForImage / imgHeight;
  300. float scale = Math.min(scaleX, scaleY);
  301. // Resize the image dimensions with the scale
  302. imgWidth *= scale;
  303. imgHeight *= scale;
  304. // Create a new page and add it to the document
  305. PDPage page = new PDPage(PDRectangle.A4);
  306. document.addPage(page);
  307. // Get the content stream for the page
  308. try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
  309. // Calculate the centered position considering the total margin around the page
  310. float x = (page.getMediaBox().getWidth() - imgWidth) / 2; // Centered without adding margin again
  311. float y = (page.getMediaBox().getHeight() - imgHeight) / 2; // Centered without adding margin again
  312. // Draw the scaled image onto the page at the correctly centered position
  313. contentStream.drawImage(image, x, y, imgWidth, imgHeight); // Apply margin here
  314. }
  315. }
  316. // Save the final document
  317. document.save(outputFile);
  318. }
  319. return null;
  320. }
  321. //图片矫正
  322. static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder().build();
  323. public static MultipartFile imageCorrection(MultipartFile file) {
  324. String name = file.getName();
  325. String originalFilename = file.getOriginalFilename();
  326. //文件后缀
  327. String extension = getExtension(file);
  328. String pre = "data:image/" + extension + ";base64,";
  329. try {
  330. String imgParam = pre + Base64Util.encode(file.getBytes());
  331. String param = "{\"image\":\"" + imgParam + "\"}";
  332. /**
  333. ** headerMap是添加的请求头,
  334. body是传入的参数,这里选择json,后端使用@RequestBody接收
  335. */
  336. String response = HttpRequest.post("http://69.172.93.148:5000/process")
  337. .header("Content-Type", "application/json")
  338. .header("x-api-key", "123456789")
  339. .body(param)
  340. .execute().body();
  341. if (StringUtils.isNotBlank(response)) {
  342. JSONObject jsonObject = JSONObject.parseObject(response);
  343. String image = jsonObject.getString("image");
  344. if (StringUtils.isNotBlank(image)){
  345. //有前缀
  346. byte[] decode = Base64.getDecoder().decode(image.split(",")[1]);
  347. // 内容类型
  348. String contentType = "image/" + extension;
  349. return new MockMultipartFile(name, originalFilename, contentType, decode);
  350. }
  351. return null;
  352. }
  353. } catch (IOException e) {
  354. e.printStackTrace();
  355. }
  356. return null;
  357. }
  358. }