package com.ruoyi.system.file;
|
|
import com.ruoyi.common.config.LegacySystemConfig;
|
import com.ruoyi.common.utils.http.HttpUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import javax.imageio.ImageIO;
|
import java.awt.*;
|
import java.awt.image.BufferedImage;
|
import java.net.HttpURLConnection;
|
import java.net.URL;
|
import java.util.*;
|
|
import java.io.*;
|
|
@Service
|
public class FileUploadServiceImpl implements IFileUploadService {
|
|
private static final Logger log = LoggerFactory.getLogger(FileUploadServiceImpl.class);
|
|
|
@Autowired
|
private LegacySystemConfig legacyConfig;
|
|
|
@Override
|
public FileUploadResponse uploadLocalFile(File file, String targetPath) {
|
if (file == null || !file.exists()) {
|
return FileUploadResponse.error("文件不存在");
|
}
|
|
try (FileInputStream fis = new FileInputStream(file)) {
|
return uploadInputStream(fis, file.getName(), targetPath);
|
} catch (IOException e) {
|
log.error("读取本地文件失败: {}", file.getAbsolutePath(), e);
|
return FileUploadResponse.error("读取文件失败: " + e.getMessage());
|
}
|
}
|
|
@Override
|
public FileUploadResponse uploadMultipartFile(MultipartFile multipartFile, String targetPath) {
|
if (multipartFile == null || multipartFile.isEmpty()) {
|
return FileUploadResponse.error("文件为空");
|
}
|
|
try (InputStream inputStream = multipartFile.getInputStream()) {
|
return uploadInputStream(inputStream, multipartFile.getOriginalFilename(), targetPath);
|
} catch (IOException e) {
|
log.error("读取MultipartFile失败", e);
|
return FileUploadResponse.error("读取文件失败: " + e.getMessage());
|
}
|
}
|
|
@Override
|
public FileUploadResponse uploadBytes(byte[] fileBytes, String fileName, String targetPath) {
|
if (fileBytes == null || fileBytes.length == 0) {
|
return FileUploadResponse.error("文件字节数组为空");
|
}
|
|
try (ByteArrayInputStream bis = new ByteArrayInputStream(fileBytes)) {
|
return uploadInputStream(bis, fileName, targetPath);
|
} catch (IOException e) {
|
log.error("处理字节数组失败", e);
|
return FileUploadResponse.error("处理文件失败: " + e.getMessage());
|
}
|
}
|
|
@Override
|
public FileUploadResponse uploadInputStream(InputStream inputStream, String fileName, String targetPath) {
|
if (inputStream == null) {
|
return FileUploadResponse.error("输入流为空");
|
}
|
|
try {
|
// 构建请求参数
|
Map<String, Object> params = new HashMap<>();
|
params.put("file", inputStream);
|
params.put("uploadFileName", targetPath);
|
|
log.info("开始上传文件到PHP接口: fileName={}, targetPath={}", fileName, targetPath);
|
|
// 调用PHP上传接口
|
String response = HttpUtils.postFile(legacyConfig.getFileUploadUrl(), params, fileName);
|
|
log.info("PHP接口响应: {}", response);
|
|
// 解析响应
|
return parseUploadResponse(response);
|
|
} catch (Exception e) {
|
log.error("上传文件到PHP接口失败: fileName={}, targetPath={}", fileName, targetPath, e);
|
return FileUploadResponse.error("上传失败: " + e.getMessage());
|
}
|
}
|
|
@Override
|
public FileUploadResponse uploadFromUrl(String fileUrl, String targetPath) {
|
if (fileUrl == null || fileUrl.trim().isEmpty()) {
|
return FileUploadResponse.error("文件URL为空");
|
}
|
|
try {
|
// 从URL下载文件
|
byte[] fileBytes = downloadFromUrl(fileUrl);
|
if (fileBytes == null || fileBytes.length == 0) {
|
return FileUploadResponse.error("从URL下载文件失败");
|
}
|
|
// 从URL中提取文件名
|
String fileName = extractFileNameFromUrl(fileUrl);
|
|
// 上传文件
|
return uploadBytes(fileBytes, fileName, targetPath);
|
|
} catch (Exception e) {
|
log.error("从URL上传文件失败: fileUrl={}, targetPath={}", fileUrl, targetPath, e);
|
return FileUploadResponse.error("从URL上传失败: " + e.getMessage());
|
}
|
}
|
|
@Override
|
public FileUploadResponse uploadFromWechat(String accessToken, String mediaId, String targetPath) {
|
if (accessToken == null || mediaId == null) {
|
return FileUploadResponse.error("微信参数为空");
|
}
|
|
try {
|
// 从微信API下载文件
|
byte[] fileBytes = downloadFromWechat(accessToken, mediaId);
|
if (fileBytes == null || fileBytes.length == 0) {
|
return FileUploadResponse.error("从微信下载文件失败");
|
}
|
|
// 生成文件名
|
String fileName = "wechat_" + mediaId + ".jpg";
|
|
// 上传文件
|
return uploadBytes(fileBytes, fileName, targetPath);
|
|
} catch (Exception e) {
|
log.error("从微信上传文件失败: mediaId={}, targetPath={}", mediaId, targetPath, e);
|
return FileUploadResponse.error("从微信上传失败: " + e.getMessage());
|
}
|
}
|
|
@Override
|
public boolean fileExists(String filePath) {
|
if (filePath == null || filePath.trim().isEmpty()) {
|
return false;
|
}
|
|
try {
|
// 构建完整的文件路径
|
String fullPath = legacyConfig.getFileServerUrl() + "/" + filePath;
|
File file = new File(fullPath);
|
return file.exists() && file.isFile();
|
} catch (Exception e) {
|
log.error("检查文件是否存在失败: {}", filePath, e);
|
return false;
|
}
|
}
|
|
@Override
|
public boolean deleteFile(String filePath) {
|
if (filePath == null || filePath.trim().isEmpty()) {
|
return false;
|
}
|
|
try {
|
// 构建完整的文件路径
|
String fullPath = legacyConfig.getFileServerUrl() + "/" + filePath;
|
File file = new File(fullPath);
|
|
if (file.exists() && file.isFile()) {
|
return file.delete();
|
}
|
return false;
|
} catch (Exception e) {
|
log.error("删除文件失败: {}", filePath, e);
|
return false;
|
}
|
}
|
|
@Override
|
public String getFileUrl(String filePath) {
|
if (filePath == null || filePath.trim().isEmpty()) {
|
return null;
|
}
|
|
// 构建文件访问URL
|
return legacyConfig.getFileServerUrl() + "/" + filePath;
|
}
|
|
/**
|
* 从URL下载文件
|
*/
|
private byte[] downloadFromUrl(String fileUrl) throws IOException {
|
URL url = new URL(fileUrl);
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
connection.setRequestMethod("GET");
|
connection.setConnectTimeout(10000);
|
connection.setReadTimeout(30000);
|
|
try (InputStream inputStream = connection.getInputStream();
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
|
byte[] buffer = new byte[4096];
|
int bytesRead;
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
outputStream.write(buffer, 0, bytesRead);
|
}
|
|
return outputStream.toByteArray();
|
} finally {
|
connection.disconnect();
|
}
|
}
|
|
/**
|
* 从微信API下载文件
|
*/
|
private byte[] downloadFromWechat(String accessToken, String mediaId) throws IOException {
|
String wechatUrl = String.format(
|
"https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s",
|
accessToken, mediaId
|
);
|
|
return downloadFromUrl(wechatUrl);
|
}
|
|
/**
|
* 从URL中提取文件名
|
*/
|
private String extractFileNameFromUrl(String fileUrl) {
|
try {
|
String fileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
|
// 移除查询参数
|
if (fileName.contains("?")) {
|
fileName = fileName.substring(0, fileName.indexOf('?'));
|
}
|
return fileName.isEmpty() ? "downloaded_file" : fileName;
|
} catch (Exception e) {
|
return "downloaded_file";
|
}
|
}
|
|
/**
|
* 解析上传响应
|
*/
|
private FileUploadResponse parseUploadResponse(String response) {
|
if (response == null || response.trim().isEmpty()) {
|
return FileUploadResponse.error("PHP接口返回空响应");
|
}
|
|
try {
|
log.info("开始解析PHP响应: {}", response);
|
|
// 根据PHP接口的实际返回格式进行解析
|
// PHP返回格式: {"success": true, "message": "文件上传成功", "data": {...}}
|
if (response.contains("\"success\":true") || response.contains("success")) {
|
// 提取文件路径和缩略图路径
|
String filePath = extractFilePathFromResponse(response);
|
String thumbnailPath = extractThumbnailPathFromResponse(response);
|
|
log.info("解析结果 - filePath: {}, thumbnailPath: {}", filePath, thumbnailPath);
|
|
// 创建响应对象
|
FileUploadResponse uploadResponse = FileUploadResponse.success(filePath, "上传成功");
|
if (thumbnailPath != null && !thumbnailPath.isEmpty()) {
|
uploadResponse.setThumbnailPath(thumbnailPath);
|
}
|
|
return uploadResponse;
|
} else {
|
// 提取错误信息
|
String errorMessage = extractErrorMessageFromResponse(response);
|
log.error("上传失败,错误信息: {}", errorMessage);
|
return FileUploadResponse.error(errorMessage != null ? errorMessage : "上传失败");
|
}
|
} catch (Exception e) {
|
log.error("解析上传响应失败: {}", response, e);
|
return FileUploadResponse.error("解析响应失败: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 从响应中提取文件路径
|
*/
|
private String extractFilePathFromResponse(String response) {
|
// 解析PHP接口返回的JSON格式: {"success": true, "data": {"filePath": "..."}}
|
try {
|
if (response.contains("\"data\":")) {
|
// 找到data对象
|
int dataStart = response.indexOf("\"data\":{") + 7;
|
if (dataStart > 6) {
|
// 在data对象中查找filePath
|
String dataSection = response.substring(dataStart);
|
if (dataSection.contains("\"filePath\":")) {
|
int start = dataSection.indexOf("\"filePath\":\"") + 12;
|
int end = dataSection.indexOf("\"", start);
|
if (start > 11 && end > start) {
|
String filePath = dataSection.substring(start, end);
|
// 去除转义字符
|
filePath = filePath.replace("\\", "");
|
log.info("提取到文件路径: {}", filePath);
|
return filePath;
|
}
|
}
|
}
|
}
|
|
// 兼容旧格式,直接查找filePath
|
if (response.contains("\"filePath\":")) {
|
int start = response.indexOf("\"filePath\":\"") + 12;
|
int end = response.indexOf("\"", start);
|
if (start > 11 && end > start) {
|
String filePath = response.substring(start, end);
|
log.info("提取到文件路径(旧格式): {}", filePath);
|
return filePath;
|
}
|
}
|
} catch (Exception e) {
|
log.error("提取文件路径失败: {}", response, e);
|
}
|
return null;
|
}
|
|
/**
|
* 从响应中提取缩略图路径
|
*/
|
private String extractThumbnailPathFromResponse(String response) {
|
// 解析PHP接口返回的JSON格式: {"success": true, "data": {"thumbnailPath": "..."}}
|
try {
|
if (response.contains("\"data\":")) {
|
// 找到data对象
|
int dataStart = response.indexOf("\"data\":{") + 7;
|
if (dataStart > 6) {
|
// 在data对象中查找thumbnailPath
|
String dataSection = response.substring(dataStart);
|
if (dataSection.contains("\"thumbnailPath\":")) {
|
int start = dataSection.indexOf("\"thumbnailPath\":\"") + 17;
|
int end = dataSection.indexOf("\"", start);
|
if (start > 16 && end > start) {
|
String thumbnailPath = dataSection.substring(start, end);
|
log.info("提取到缩略图路径: {}", thumbnailPath);
|
thumbnailPath = thumbnailPath.replace("\\", ""); // 去除转义字符
|
return thumbnailPath;
|
}
|
}
|
}
|
}
|
|
// 兼容旧格式,直接查找thumbnailPath
|
if (response.contains("\"thumbnailPath\":")) {
|
int start = response.indexOf("\"thumbnailPath\":\"") + 17;
|
int end = response.indexOf("\"", start);
|
if (start > 16 && end > start) {
|
String thumbnailPath = response.substring(start, end);
|
log.info("提取到缩略图路径(旧格式): {}", thumbnailPath);
|
thumbnailPath = thumbnailPath.replace("\\", ""); // 去除转义字符
|
return thumbnailPath;
|
}
|
}
|
} catch (Exception e) {
|
log.error("提取缩略图路径失败: {}", response, e);
|
}
|
return null;
|
}
|
|
/**
|
* 从响应中提取错误信息
|
*/
|
private String extractErrorMessageFromResponse(String response) {
|
// 解析PHP接口返回的JSON格式: {"success": false, "message": "错误信息"}
|
try {
|
if (response.contains("\"message\":")) {
|
int start = response.indexOf("\"message\":\"") + 11;
|
int end = response.indexOf("\"", start);
|
if (start > 10 && end > start) {
|
String errorMessage = response.substring(start, end);
|
log.info("提取到错误信息: {}", errorMessage);
|
return errorMessage;
|
}
|
}
|
} catch (Exception e) {
|
log.error("提取错误信息失败: {}", response, e);
|
}
|
return null;
|
}
|
|
/**
|
* 生成缩略图
|
*
|
* @param sourcePath 源图片路径
|
* @param targetPath 目标缩略图路径
|
* @param width 宽度
|
* @param height 高度(0表示按比例缩放)
|
* @return 是否生成成功
|
*/
|
private boolean createThumbnail(String sourcePath, String targetPath, int width, int height) {
|
try {
|
// 读取源图片
|
BufferedImage sourceImage = ImageIO.read(new File(sourcePath));
|
if (sourceImage == null) {
|
log.error("无法读取源图片:{}", sourcePath);
|
return false;
|
}
|
|
// 计算缩略图尺寸
|
int sourceWidth = sourceImage.getWidth();
|
int sourceHeight = sourceImage.getHeight();
|
|
if (height == 0) {
|
height = (int) Math.floor((double) width / sourceWidth * sourceHeight);
|
} else if (width == 0) {
|
width = (int) Math.floor((double) height / sourceHeight * sourceWidth);
|
}
|
|
// 创建缩略图
|
BufferedImage thumbnailImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
Graphics2D g2d = thumbnailImage.createGraphics();
|
|
// 设置渲染质量
|
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
// 绘制缩略图
|
g2d.drawImage(sourceImage, 0, 0, width, height, null);
|
g2d.dispose();
|
|
// 保存缩略图
|
String extension = getFileExtension(sourcePath);
|
return ImageIO.write(thumbnailImage, extension, new File(targetPath));
|
|
} catch (Exception e) {
|
log.error("生成缩略图失败:sourcePath={}, targetPath={}", sourcePath, targetPath, e);
|
return false;
|
}
|
}
|
|
/**
|
* 获取文件扩展名
|
*/
|
private String getFileExtension(String filePath) {
|
int lastDotIndex = filePath.lastIndexOf('.');
|
if (lastDotIndex > 0) {
|
return filePath.substring(lastDotIndex + 1).toLowerCase();
|
}
|
return "jpg"; // 默认扩展名
|
}
|
|
/**
|
* 本地文件上传(包含缩略图生成)
|
*/
|
public FileUploadResponse uploadLocalFileWithThumbnail(File file, String targetPath) {
|
if (file == null || !file.exists()) {
|
return FileUploadResponse.error("文件不存在");
|
}
|
|
try {
|
// 检查是否为图片文件
|
String fileName = file.getName().toLowerCase();
|
boolean isImage = fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") ||
|
fileName.endsWith(".png") || fileName.endsWith(".gif");
|
|
// 上传原文件
|
FileUploadResponse uploadResponse = uploadLocalFile(file, targetPath);
|
if (!uploadResponse.isSuccess()) {
|
return uploadResponse;
|
}
|
|
log.info("上传文件成功 文件:{} 缩略:{}",uploadResponse.getFilePath(),uploadResponse.getThumbnailPath());
|
return uploadResponse;
|
|
} catch (Exception e) {
|
log.error("上传文件并生成缩略图失败:{}", file.getAbsolutePath(), e);
|
return FileUploadResponse.error("上传失败:" + e.getMessage());
|
}
|
}
|
|
/**
|
* MultipartFile上传(包含缩略图生成)
|
*/
|
public FileUploadResponse uploadMultipartFileWithThumbnail(MultipartFile multipartFile, String targetPath) {
|
if (multipartFile == null || multipartFile.isEmpty()) {
|
return FileUploadResponse.error("文件为空");
|
}
|
|
try {
|
// 检查是否为图片文件
|
String originalFilename = multipartFile.getOriginalFilename();
|
if (originalFilename != null) {
|
String fileName = originalFilename.toLowerCase();
|
boolean isImage = fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") ||
|
fileName.endsWith(".png") || fileName.endsWith(".gif");
|
|
// 上传原文件
|
FileUploadResponse uploadResponse = uploadMultipartFile(multipartFile, targetPath);
|
if (!uploadResponse.isSuccess()) {
|
return uploadResponse;
|
}
|
|
|
|
return uploadResponse;
|
}
|
|
return uploadMultipartFile(multipartFile, targetPath);
|
|
} catch (Exception e) {
|
log.error("上传MultipartFile并生成缩略图失败", e);
|
return FileUploadResponse.error("上传失败:" + e.getMessage());
|
}
|
}
|
|
/**
|
* 生成缩略图路径
|
*/
|
private String generateThumbnailPath(String originalPath) {
|
if (originalPath == null || originalPath.isEmpty()) {
|
return null;
|
}
|
|
// 在文件名前添加 "s_" 前缀
|
int lastSlashIndex = originalPath.lastIndexOf('/');
|
if (lastSlashIndex >= 0) {
|
String directory = originalPath.substring(0, lastSlashIndex + 1);
|
String fileName = originalPath.substring(lastSlashIndex + 1);
|
return directory + "s_" + fileName;
|
} else {
|
return "s_" + originalPath;
|
}
|
}
|
}
|