package com.ruoyi.common.utils.file; import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.uuid.IdUtils; import net.coobird.thumbnailator.Thumbnails; import org.apache.commons.fileupload.disk.DiskFileItem; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.tika.Tika; import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.commons.CommonsMultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; /** * 文件处理工具类 * * @author ruoyi */ public class FileUtils { public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; /** * 输出指定文件的byte数组 * * @param filePath 文件路径 * @param os 输出流 * @return */ public static void writeBytes(String filePath, OutputStream os) throws IOException { FileInputStream fis = null; try { File file = new File(filePath); if (!file.exists()) { throw new FileNotFoundException(filePath); } fis = new FileInputStream(file); byte[] b = new byte[1024]; int length; while ((length = fis.read(b)) > 0) { os.write(b, 0, length); } } catch (IOException e) { throw e; } finally { IOUtils.close(os); IOUtils.close(fis); } } /** * 写数据到文件中 * * @param data 数据 * @return 目标文件 * @throws IOException IO异常 */ public static String writeImportBytes(byte[] data) throws IOException { return writeBytes(data, RuoYiConfig.getImportPath()); } /** * 写数据到文件中 * * @param data 数据 * @param uploadDir 目标文件 * @return 目标文件 * @throws IOException IO异常 */ public static String writeBytes(byte[] data, String uploadDir) throws IOException { FileOutputStream fos = null; String pathName = ""; try { String extension = getFileExtendName(data); pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); fos = new FileOutputStream(file); fos.write(data); } finally { IOUtils.close(fos); } return FileUploadUtils.getPathFileName(uploadDir, pathName); } /** * 删除文件 * * @param filePath 文件 * @return */ public static boolean deleteFile(String filePath) { boolean flag = false; File file = new File(filePath); // 路径为文件且不为空则进行删除 if (file.isFile() && file.exists()) { flag = file.delete(); } return flag; } /** * 文件名称验证 * * @param filename 文件名称 * @return true 正常 false 非法 */ public static boolean isValidFilename(String filename) { return filename.matches(FILENAME_PATTERN); } /** * 检查文件是否可下载 * * @param resource 需要下载的文件 * @return true 正常 false 非法 */ public static boolean checkAllowDownload(String resource) { // 禁止目录上跳级别 if (StringUtils.contains(resource, "..")) { return false; } // 检查允许下载的文件规则 if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) { return true; } // 不在允许下载的文件规则 return false; } /** * 下载文件名重新编码 * * @param request 请求对象 * @param fileName 文件名 * @return 编码后的文件名 */ public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException { final String agent = request.getHeader("USER-AGENT"); String filename = fileName; if (agent.contains("MSIE")) { // IE浏览器 filename = URLEncoder.encode(filename, "utf-8"); filename = filename.replace("+", " "); } else if (agent.contains("Firefox")) { // 火狐浏览器 filename = new String(fileName.getBytes(), "ISO8859-1"); } else if (agent.contains("Chrome")) { // google浏览器 filename = URLEncoder.encode(filename, "utf-8"); } else { // 其它浏览器 filename = URLEncoder.encode(filename, "utf-8"); } return filename; } /** * 下载文件名重新编码 * * @param response 响应对象 * @param realFileName 真实文件名 */ public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException { String percentEncodedFileName = percentEncode(realFileName); StringBuilder contentDispositionValue = new StringBuilder(); contentDispositionValue.append("attachment; filename=") .append(percentEncodedFileName) .append(";") .append("filename*=") .append("utf-8''") .append(percentEncodedFileName); response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); response.setHeader("Content-disposition", contentDispositionValue.toString()); response.setHeader("download-filename", percentEncodedFileName); } /** * 百分号编码工具方法 * * @param s 需要百分号编码的字符串 * @return 百分号编码后的字符串 */ public static String percentEncode(String s) throws UnsupportedEncodingException { String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); return encode.replaceAll("\\+", "%20"); } /** * 获取图像后缀 * * @param photoByte 图像数据 * @return 后缀名 */ public static String getFileExtendName(byte[] photoByte) { String strFileExtendName = "jpg"; if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) { strFileExtendName = "gif"; } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) { strFileExtendName = "jpg"; } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) { strFileExtendName = "bmp"; } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) { strFileExtendName = "png"; } return strFileExtendName; } /** * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png * * @param fileName 路径名称 * @return 没有文件路径的名称 */ public static String getName(String fileName) { if (fileName == null) { return null; } int lastUnixPos = fileName.lastIndexOf('/'); int lastWindowsPos = fileName.lastIndexOf('\\'); int index = Math.max(lastUnixPos, lastWindowsPos); return fileName.substring(index + 1); } /** * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi * * @param fileName 路径名称 * @return 没有文件路径和后缀的名称 */ public static String getNameNotSuffix(String fileName) { if (fileName == null) { return null; } String baseName = FilenameUtils.getBaseName(fileName); return baseName; } /** * 区分上传的文件是什么类型 * * @param file 文件流 * @return 类型 */ public static String getFileType(MultipartFile file) { Tika tika = new Tika(); try { String mimeType = tika.detect(file.getInputStream()); if (mimeType.startsWith("image/")) { return "image"; } else if (mimeType.startsWith("audio/")) { return "audio"; } else if (mimeType.startsWith("video/")) { return "video"; } else { return "other"; } } catch (IOException e) { e.printStackTrace(); return "other"; } } /** * 获取文件类型(图片、音频、视频等) * * @param fileUrl 文件的URL * @return 文件类型 */ public static String getFileTypeFromUrl(String fileUrl) throws IOException { URL url = new URL(fileUrl); // 设置连接属性 java.net.HttpURLConnection connection = null; try { // 检查是否需要设置代理 String proxyHost = System.getProperty("http.proxyHost"); String proxyPort = System.getProperty("http.proxyPort"); if (StringUtils.isNotEmpty(proxyHost) && StringUtils.isNotEmpty(proxyPort)) { // 使用系统配置的代理 java.net.Proxy proxy = new java.net.Proxy( java.net.Proxy.Type.HTTP, new java.net.InetSocketAddress(proxyHost, Integer.parseInt(proxyPort)) ); connection = (java.net.HttpURLConnection) url.openConnection(proxy); } else { connection = (java.net.HttpURLConnection) url.openConnection(); } // 设置连接参数 connection.setConnectTimeout(5000); // 5秒连接超时 connection.setReadTimeout(5000); // 5秒读取超时 connection.setRequestMethod("GET"); connection.setDoInput(true); // 尝试连接 connection.connect(); try (InputStream inputStream = connection.getInputStream()) { Tika tika = new Tika(); String mimeType = tika.detect(inputStream); if (mimeType.startsWith("image/")) { return "image"; } else if (mimeType.startsWith("audio/")) { return "audio"; } else if (mimeType.startsWith("video/")) { return "video"; } else { return "other"; } } } catch (java.net.SocketException e) { // 处理网络权限错误 System.err.println("网络连接权限被拒绝: " + e.getMessage()); // 尝试从URL路径推断文件类型 String path = url.getPath(); String extension = FilenameUtils.getExtension(path).toLowerCase(); // 根据扩展名判断文件类型 if (MimeTypeUtils.isImage(extension)) { return "image"; } else if (MimeTypeUtils.isAudio(extension)) { return "audio"; } else if (MimeTypeUtils.isVideo(extension)) { return "video"; } else { return "other"; } } finally { if (connection != null) { connection.disconnect(); } } } /** * 截取视频的指定时间帧,生成图片文件 * * @param source 源文件 * @param file 图片文件 * @param time 截图时间 HH:mm:ss.[SSS] * @throws IOException * @throws InterruptedException */ public static boolean screenShots(String source, String file, String time) throws IOException, InterruptedException { List<String> commands = new ArrayList<>(); commands.add("ffmpeg"); commands.add("-i"); commands.add(source); commands.add("-ss"); commands.add(time); commands.add("-y"); commands.add("-q:v"); commands.add("1"); commands.add("-frames:v"); commands.add("1"); commands.add("-f"); commands.add("image2"); commands.add(file); Process process = new ProcessBuilder(commands).start(); // 读取进程标准输出 new Thread(() -> { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line = null; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } } catch (IOException e) { } }).start(); // 读取进程异常输出 new Thread(() -> { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { String line = null; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } } catch (IOException e) { } }).start(); if (process.waitFor() != 0) { return false; } OssUtils.uploadFile(file); return true; } /** * File转换MultipartFile * * @param file 文件 * @return MultipartFile * @throws Exception 异常 */ public static MultipartFile convertFileToMultipartFile(File file) throws Exception { DiskFileItem fileItem = new DiskFileItem("file", "image/jpg", true, file.getName(), (int) file.length(), file.getParentFile()); try (FileInputStream input = new FileInputStream(file); OutputStream os = fileItem.getOutputStream()) { IOUtils.copy(input, os); } return new CommonsMultipartFile(fileItem); } public static MultipartFile convertToJpg(MultipartFile file) throws IOException { byte[] bytes = file.getBytes(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Thumbnails.of(new ByteArrayInputStream(bytes)) .scale(1) .outputFormat("jpg") .toOutputStream(outputStream); return new MockMultipartFile( file.getName(), file.getOriginalFilename().replaceAll("\\.[^.]+$", ".jpg"), "image/jpeg", outputStream.toByteArray() ); } /** * 清理临时文件 * * @param fileName 文件名称 */ public static void cleanTempDir(String fileName) { String tempDirPath = System.getProperty("java.io.tmpdir"); File tempDir = new File(tempDirPath); if (tempDir.exists() && tempDir.isDirectory()) { File[] files = tempDir.listFiles(); if (files != null) { for (File file : files) { // 检查是否是临时文件,并尝试删除 if (file.isFile() && file.getName().endsWith(".tmp") || file.getName().startsWith(fileName)) { boolean deleted = file.delete(); if (deleted) { System.out.println("删除临时文件成功: " + file.getName()); } else { System.out.println("删除临时文件失败: " + file.getName()); } } } } } } }