java设计模式-工厂模式(文件上传) - 实践
2025-09-21 15:33 tlnshuju 阅读(0) 评论(0) 收藏 举报工厂模式是一种创建对象的设计模式,它将对象的创建和使用分离,通过一个工厂类来负责创建对象。
我以文件上传策略工厂为例
/**
* 文件上传策略工厂
*/
@Slf4j
@Component
public class FileUploadStrategyFactory {
private final Map strategyMap = new ConcurrentHashMap<>();
@Autowired
public FileUploadStrategyFactory(List strategies) {
for (FileUploadStrategy strategy : strategies) {
strategyMap.put(strategy.getType(), strategy);
}
}
/**
* 获取文件上传策略
*
* @param type 策略类型
* @return 文件上传策略
*/
public FileUploadStrategy getStrategy(String type) {
FileUploadStrategy strategy = strategyMap.get(type);
if (strategy == null) {
throw new IllegalArgumentException("不支持的文件上传类型: " + type);
}
return strategy;
}
/**
* 获取所有策略类型
*
* @return 策略类型列表
*/
public java.util.Set getAllTypes() {
return strategyMap.keySet();
}
}
/**
* 文件上传策略接口
*/
public interface FileUploadStrategy {
/**
* 获取策略类型
*/
String getType();
/**
* 上传文件
*
* @param file 文件
* @param config OSS配置
* @return 上传结果
*/
FileUploadResult upload(MultipartFile file, SysOssConfig config);
/**
* 上传文件流
*
* @param inputStream 文件流
* @param fileName 文件名
* @param contentType 文件类型
* @param config OSS配置
* @return 上传结果
*/
FileUploadResult upload(InputStream inputStream, String fileName, String contentType, SysOssConfig config);
/**
* 删除文件
*
* @param fileName 文件名
* @param config OSS配置
* @return 删除结果
*/
boolean delete(String fileName, SysOssConfig config);
/**
* 获取文件预签名URL
*
* @param fileName 文件名
* @param config OSS配置
* @param expireTime 过期时间(秒)
* @return 预签名URL
*/
String getPresignedUrl(String fileName, SysOssConfig config, int expireTime);
/**
* 获取文件下载流
*
* @param fileName 文件名
* @param config OSS配置
* @return 文件输入流
* @throws IOException IO异常
*/
InputStream getFileInputStream(String fileName, SysOssConfig config) throws IOException;
/**
* 获取文件预览信息
*
* @param fileName 文件名
* @param config OSS配置
* @return 预览信息
* @throws IOException IO异常
*/
FilePreviewInfo getPreviewInfo(String fileName, String originalName, String contentType, long size, SysOssConfig config) throws IOException;
}
MinIO文件上传策略
/**
* MinIO文件上传策略
*/
@Slf4j
@Component
public class MinioFileUploadStrategy implements FileUploadStrategy {
@Override
public String getType() {
return "minio";
}
@Override
public FileUploadResult upload(MultipartFile file, SysOssConfig config) {
try {
MinioClient minioClient = createMinioClient(config);
String originalName = file.getOriginalFilename();
String fileName = generateFileName(originalName);
String objectName = buildObjectName(fileName, config);
// 确保桶存在
ensureBucketExists(minioClient, config.getBucketName());
// 上传文件
minioClient.putObject(
PutObjectArgs.builder()
.bucket(config.getBucketName())
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build()
);
// 构建访问URL
String url = buildAccessUrl(config, objectName);
return new FileUploadResult()
.setFileName(fileName)
.setOriginalName(originalName)
.setFileSuffix(FileUtils.getFileExtension(originalName))
.setUrl(url)
.setSize(file.getSize())
.setFilePath(objectName)
.setContentType(file.getContentType())
.setConfigKey(config.getConfigKey())
.setUploadStatus(1);
} catch (Exception e) {
log.error("MinIO文件上传失败", e);
throw new RuntimeException("MinIO文件上传失败: " + e.getMessage());
}
}
@Override
public FileUploadResult upload(InputStream inputStream, String fileName, String contentType, SysOssConfig config) {
try {
MinioClient minioClient = createMinioClient(config);
String objectName = buildObjectName(fileName, config);
// 确保桶存在
ensureBucketExists(minioClient, config.getBucketName());
// 由于无法直接获取InputStream的大小,使用-1让MinIO自动处理
minioClient.putObject(
PutObjectArgs.builder()
.bucket(config.getBucketName())
.object(objectName)
.stream(inputStream, -1, 10485760) // 10MB part size
.contentType(contentType)
.build()
);
// 获取文件信息
StatObjectResponse stat = minioClient.statObject(
StatObjectArgs.builder()
.bucket(config.getBucketName())
.object(objectName)
.build()
);
// 构建访问URL
String url = buildAccessUrl(config, objectName);
return new FileUploadResult()
.setFileName(fileName)
.setOriginalName(fileName)
.setFileSuffix(FileUtils.getFileExtension(fileName))
.setUrl(url)
.setSize(stat.size())
.setFilePath(objectName)
.setContentType(contentType)
.setConfigKey(config.getConfigKey())
.setUploadStatus(1);
} catch (Exception e) {
log.error("MinIO文件流上传失败", e);
throw new RuntimeException("MinIO文件流上传失败: " + e.getMessage());
}
}
@Override
public boolean delete(String fileName, SysOssConfig config) {
try {
MinioClient minioClient = createMinioClient(config);
String objectName = config.getPrefix() + "/" + fileName;
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(config.getBucketName())
.object(objectName)
.build()
);
return true;
} catch (Exception e) {
log.error("删除MinIO文件失败: {}", fileName, e);
return false;
}
}
@Override
public String getPresignedUrl(String fileName, SysOssConfig config, int expireTime) {
try {
MinioClient minioClient = createMinioClient(config);
String objectName = config.getPrefix() + "/" + fileName;
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(config.getBucketName())
.object(objectName)
.expiry(expireTime, TimeUnit.SECONDS)
.build()
);
} catch (Exception e) {
log.error("获取MinIO预签名URL失败: {}", fileName, e);
return null;
}
}
@Override
public InputStream getFileInputStream(String fileName, SysOssConfig config) throws IOException {
try {
MinioClient minioClient = createMinioClient(config);
// 确保桶存在
ensureBucketExists(minioClient, config.getBucketName());
// 获取文件流
return minioClient.getObject(
GetObjectArgs.builder()
.bucket(config.getBucketName())
.object(fileName)
.build()
);
} catch (Exception e) {
log.error("获取MinIO文件流失败: {}", e.getMessage(), e);
throw new IOException("获取MinIO文件流失败: " + e.getMessage());
}
}
@Override
public FilePreviewInfo getPreviewInfo(String fileName, String originalName, String contentType, long size, SysOssConfig config) throws IOException {
try {
MinioClient minioClient = createMinioClient(config);
// 确保桶存在
ensureBucketExists(minioClient, config.getBucketName());
// 获取预签名URL,有效期10分钟
String url = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.bucket(config.getBucketName())
.object(fileName)
.method(Method.GET)
.expiry(10, TimeUnit.MINUTES)
.build()
);
// 构建预览信息
FilePreviewInfo previewInfo = new FilePreviewInfo()
.setPreviewUrl(url)
.setContentType(contentType)
.setOriginalName(originalName)
.setSize(size);
// 设置预览类型
if (isImageFile(contentType)) {
// 图片直接预览
previewInfo.setPreviewType(1);
} else if (isPdfFile(contentType)) {
// PDF直接预览
previewInfo.setPreviewType(3);
} else if (isOfficeFile(contentType)) {
// Office文档需要预览服务
previewInfo.setPreviewType(2);
previewInfo.setNeedConvert(true);
} else if (isVideoFile(contentType)) {
// 视频文件
previewInfo.setPreviewType(4);
} else if (isAudioFile(contentType)) {
// 音频文件
previewInfo.setPreviewType(5);
} else if (isTextFile(contentType, originalName)) {
// 文本文件
previewInfo.setPreviewType(6);
} else {
// 不支持预览
previewInfo.setPreviewType(0);
}
return previewInfo;
} catch (Exception e) {
log.error("获取MinIO预览信息失败: {}", e.getMessage(), e);
throw new IOException("获取MinIO预览信息失败: " + e.getMessage());
}
}
/**
* 判断是否为文本文件
*/
private boolean isTextFile(String contentType, String fileName) {
// 根据内容类型判断
if (contentType != null && (
contentType.equals("text/plain") ||
contentType.equals("text/html") ||
contentType.equals("text/css") ||
contentType.equals("text/javascript") ||
contentType.equals("application/json") ||
contentType.equals("application/xml"))) {
return true;
}
// 根据文件扩展名判断
if (fileName != null) {
String lowerName = fileName.toLowerCase();
return lowerName.endsWith(".txt") ||
lowerName.endsWith(".log") ||
lowerName.endsWith(".ini") ||
lowerName.endsWith(".json") ||
lowerName.endsWith(".xml") ||
lowerName.endsWith(".md") ||
lowerName.endsWith(".csv");
}
return false;
}
private MinioClient createMinioClient(SysOssConfig config) {
return MinioClient.builder()
.endpoint(config.getEndpoint())
.credentials(config.getAccessKey(), config.getSecretKey())
.region(config.getRegion())
.build();
}
private void ensureBucketExists(MinioClient minioClient, String bucketName) throws Exception {
boolean exists = minioClient.bucketExists(
BucketExistsArgs.builder()
.bucket(bucketName)
.build()
);
if (!exists) {
minioClient.makeBucket(
MakeBucketArgs.builder()
.bucket(bucketName)
.build()
);
log.info("创建MinIO桶: {}", bucketName);
}
}
private String generateFileName(String originalName) {
String extension = FileUtils.getFileExtension(originalName);
String uuid = UUID.randomUUID().toString().replace("-", "");
return uuid + (StringUtils.hasText(extension) ? "." + extension : "");
}
private String buildObjectName(String fileName, SysOssConfig config) {
String prefix = StringUtils.hasText(config.getPrefix()) ? config.getPrefix() : "upload";
String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
return prefix + "/" + datePath + "/" + fileName;
}
private String buildAccessUrl(SysOssConfig config, String objectName) {
String baseUrl = config.getDomain();
if (!StringUtils.hasText(baseUrl)) {
baseUrl = config.getEndpoint();
}
// 确保URL以/结尾
if (!baseUrl.endsWith("/")) {
baseUrl += "/";
}
return baseUrl + config.getBucketName() + "/" + objectName;
}
/**
* 判断是否为图片文件
*/
private boolean isImageFile(String contentType) {
return contentType != null && contentType.startsWith("image/");
}
/**
* 判断是否为PDF文件
*/
private boolean isPdfFile(String contentType) {
return contentType != null && contentType.equals("application/pdf");
}
/**
* 判断是否为Office文件
*/
private boolean isOfficeFile(String contentType) {
if (contentType == null) return false;
return contentType.contains("word") ||
contentType.contains("excel") ||
contentType.contains("powerpoint") ||
contentType.contains("ms-excel") ||
contentType.contains("ms-word") ||
contentType.contains("ms-powerpoint") ||
contentType.contains("openxmlformats");
}
/**
* 判断是否为视频文件
*/
private boolean isVideoFile(String contentType) {
return contentType != null && contentType.startsWith("video/");
}
/**
* 判断是否为音频文件
*/
private boolean isAudioFile(String contentType) {
return contentType != null && contentType.startsWith("audio/");
}
}
阿里云OSS文件上传策略
/**
* 阿里云OSS文件上传策略
*/
@Slf4j
@Component
public class AliyunOssFileUploadStrategy implements FileUploadStrategy {
@Override
public String getType() {
return "aliyun";
}
@Override
public FileUploadResult upload(MultipartFile file, SysOssConfig config) {
OSS ossClient = null;
try {
ossClient = createOssClient(config);
String originalName = file.getOriginalFilename();
String fileName = generateFileName(originalName);
String objectName = buildObjectName(fileName, config);
// 创建上传Object的Metadata
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(file.getSize());
metadata.setContentType(file.getContentType());
metadata.setCacheControl("no-cache");
metadata.setHeader("Pragma", "no-cache");
metadata.setContentDisposition("inline;filename=" + originalName);
// 上传文件
PutObjectRequest putObjectRequest = new PutObjectRequest(
config.getBucketName(), objectName, file.getInputStream(), metadata);
ossClient.putObject(putObjectRequest);
// 构建访问URL
String url = buildAccessUrl(config, objectName);
return new FileUploadResult()
.setFileName(fileName)
.setOriginalName(originalName)
.setFileSuffix(FileUtils.getFileExtension(originalName))
.setUrl(url)
.setSize(file.getSize())
.setFilePath(objectName)
.setContentType(file.getContentType())
.setConfigKey(config.getConfigKey())
.setUploadStatus(1);
} catch (Exception e) {
log.error("阿里云OSS文件上传失败", e);
throw new RuntimeException("阿里云OSS文件上传失败: " + e.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
@Override
public FileUploadResult upload(InputStream inputStream, String fileName, String contentType, SysOssConfig config) {
OSS ossClient = null;
try {
ossClient = createOssClient(config);
String objectName = buildObjectName(fileName, config);
// 创建上传Object的Metadata
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(contentType);
metadata.setCacheControl("no-cache");
metadata.setHeader("Pragma", "no-cache");
metadata.setContentDisposition("inline;filename=" + fileName);
// 上传文件
PutObjectRequest putObjectRequest = new PutObjectRequest(
config.getBucketName(), objectName, inputStream, metadata);
ossClient.putObject(putObjectRequest);
// 获取文件信息
com.aliyun.oss.model.ObjectMetadata objMetadata = ossClient.getObjectMetadata(config.getBucketName(), objectName);
long size = objMetadata.getContentLength();
// 构建访问URL
String url = buildAccessUrl(config, objectName);
return new FileUploadResult()
.setFileName(fileName)
.setOriginalName(fileName)
.setFileSuffix(FileUtils.getFileExtension(fileName))
.setUrl(url)
.setSize(size)
.setFilePath(objectName)
.setContentType(contentType)
.setConfigKey(config.getConfigKey())
.setUploadStatus(1);
} catch (Exception e) {
log.error("阿里云OSS文件流上传失败", e);
throw new RuntimeException("阿里云OSS文件流上传失败: " + e.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
@Override
public boolean delete(String fileName, SysOssConfig config) {
OSS ossClient = null;
try {
ossClient = createOssClient(config);
String objectName = config.getPrefix() + "/" + fileName;
ossClient.deleteObject(config.getBucketName(), objectName);
return true;
} catch (Exception e) {
log.error("删除阿里云OSS文件失败: {}", fileName, e);
return false;
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
@Override
public String getPresignedUrl(String fileName, SysOssConfig config, int expireTime) {
OSS ossClient = null;
try {
ossClient = createOssClient(config);
String objectName = config.getPrefix() + "/" + fileName;
// 设置过期时间
Date expiration = new Date(System.currentTimeMillis() + expireTime * 1000L);
return ossClient.generatePresignedUrl(config.getBucketName(), objectName, expiration).toString();
} catch (Exception e) {
log.error("获取阿里云OSS预签名URL失败: {}", fileName, e);
return null;
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
@Override
public InputStream getFileInputStream(String fileName, SysOssConfig config) throws IOException {
OSS ossClient = null;
try {
ossClient = createOssClient(config);
// 获取文件流
OSSObject ossObject = ossClient.getObject(config.getBucketName(), fileName);
return ossObject.getObjectContent();
} catch (Exception e) {
log.error("获取阿里云OSS文件流失败: {}", e.getMessage(), e);
throw new IOException("获取阿里云OSS文件流失败: " + e.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
@Override
public FilePreviewInfo getPreviewInfo(String fileName, String originalName, String contentType, long size, SysOssConfig config) throws IOException {
OSS ossClient = null;
try {
ossClient = createOssClient(config);
// 检查文件是否存在
boolean exists = ossClient.doesObjectExist(config.getBucketName(), fileName);
if (!exists) {
log.error("阿里云OSS文件不存在: {}", fileName);
throw new IOException("文件不存在: " + fileName);
}
// 获取签名URL,默认10分钟有效期
Date expiration = new Date(System.currentTimeMillis() + 10 * 60 * 1000);
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(config.getBucketName(), fileName);
request.setExpiration(expiration);
URL signedUrl = ossClient.generatePresignedUrl(request);
String url = signedUrl.toString();
// 构建预览信息
FilePreviewInfo previewInfo = new FilePreviewInfo()
.setPreviewUrl(url)
.setContentType(contentType)
.setOriginalName(originalName)
.setSize(size);
// 设置预览类型
if (isImageFile(contentType)) {
// 图片直接预览
previewInfo.setPreviewType(1);
} else if (isPdfFile(contentType)) {
// PDF直接预览
previewInfo.setPreviewType(3);
} else if (isOfficeFile(contentType)) {
// Office文档需要预览服务
previewInfo.setPreviewType(2);
previewInfo.setNeedConvert(true);
} else if (isVideoFile(contentType)) {
// 视频文件
previewInfo.setPreviewType(4);
} else if (isAudioFile(contentType)) {
// 音频文件
previewInfo.setPreviewType(5);
} else if (isTextFile(contentType, originalName)) {
// 文本文件
previewInfo.setPreviewType(6);
} else {
// 不支持预览
previewInfo.setPreviewType(0);
}
return previewInfo;
} catch (Exception e) {
log.error("获取阿里云OSS预览信息失败: {}", e.getMessage(), e);
throw new IOException("获取阿里云OSS预览信息失败: " + e.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
/**
* 判断是否为文本文件
*/
private boolean isTextFile(String contentType, String fileName) {
// 根据内容类型判断
if (contentType != null && (
contentType.equals("text/plain") ||
contentType.equals("text/html") ||
contentType.equals("text/css") ||
contentType.equals("text/javascript") ||
contentType.equals("application/json") ||
contentType.equals("application/xml"))) {
return true;
}
// 根据文件扩展名判断
if (fileName != null) {
String lowerName = fileName.toLowerCase();
return lowerName.endsWith(".txt") ||
lowerName.endsWith(".log") ||
lowerName.endsWith(".ini") ||
lowerName.endsWith(".json") ||
lowerName.endsWith(".xml") ||
lowerName.endsWith(".md") ||
lowerName.endsWith(".csv");
}
return false;
}
private OSS createOssClient(SysOssConfig config) {
String endpoint = config.getEndpoint();
if (config.getIsHttps() != null && config.getIsHttps() == 1) {
if (!endpoint.startsWith("https://")) {
endpoint = "https://" + endpoint.replace("http://", "");
}
} else {
if (!endpoint.startsWith("http://")) {
endpoint = "http://" + endpoint.replace("https://", "");
}
}
return new OSSClientBuilder().build(endpoint, config.getAccessKey(), config.getSecretKey());
}
private String generateFileName(String originalName) {
String extension = FileUtils.getFileExtension(originalName);
String uuid = UUID.randomUUID().toString().replace("-", "");
return uuid + (StringUtils.hasText(extension) ? "." + extension : "");
}
private String buildObjectName(String fileName, SysOssConfig config) {
String prefix = StringUtils.hasText(config.getPrefix()) ? config.getPrefix() : "upload";
String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
return prefix + "/" + datePath + "/" + fileName;
}
private String buildAccessUrl(SysOssConfig config, String objectName) {
String baseUrl = config.getDomain();
if (!StringUtils.hasText(baseUrl)) {
String endpoint = config.getEndpoint();
if (config.getIsHttps() != null && config.getIsHttps() == 1) {
baseUrl = "https://" + config.getBucketName() + "." + endpoint.replace("https://", "").replace("http://", "");
} else {
baseUrl = "http://" + config.getBucketName() + "." + endpoint.replace("https://", "").replace("http://", "");
}
}
// 确保URL以/结尾
if (!baseUrl.endsWith("/")) {
baseUrl += "/";
}
return baseUrl + objectName;
}
/**
* 判断是否为图片文件
*/
private boolean isImageFile(String contentType) {
return contentType != null && contentType.startsWith("image/");
}
/**
* 判断是否为PDF文件
*/
private boolean isPdfFile(String contentType) {
return contentType != null && contentType.equals("application/pdf");
}
/**
* 判断是否为Office文件
*/
private boolean isOfficeFile(String contentType) {
if (contentType == null) return false;
return contentType.contains("word") ||
contentType.contains("excel") ||
contentType.contains("powerpoint") ||
contentType.contains("ms-excel") ||
contentType.contains("ms-word") ||
contentType.contains("ms-powerpoint") ||
contentType.contains("openxmlformats");
}
/**
* 判断是否为视频文件
*/
private boolean isVideoFile(String contentType) {
return contentType != null && contentType.startsWith("video/");
}
/**
* 判断是否为音频文件
*/
private boolean isAudioFile(String contentType) {
return contentType != null && contentType.startsWith("audio/");
}
}
本地文件上传策略
/**
* 本地文件上传策略
*/
@Slf4j
@Component
public class LocalFileUploadStrategy implements FileUploadStrategy {
@Value("${file.upload.path:./upload}")
private String uploadPath;
@Value("${server.servlet.context-path:}")
private String contextPath;
@Value("${server.port:8080}")
private String serverPort;
@Override
public String getType() {
return "local";
}
@Override
public FileUploadResult upload(MultipartFile file, SysOssConfig config) {
try {
// 初始化上传路径
initUploadPath();
String originalName = file.getOriginalFilename();
String fileName = generateFileName(originalName);
String filePath = buildFilePath(fileName, config);
String fullPath = getNormalizedPath(uploadPath, filePath);
log.info("保存文件到: {}", fullPath);
// 创建目录
createDirectories(fullPath);
// 保存文件
File targetFile = new File(fullPath);
try {
file.transferTo(targetFile);
} catch (IOException e) {
log.error("保存文件失败: {}", e.getMessage());
// 尝试使用替代方法保存
try (FileOutputStream out = new FileOutputStream(targetFile)) {
out.write(file.getBytes());
}
}
// 确认文件已保存成功
if (!targetFile.exists()) {
throw new IOException("文件保存失败,无法在目标路径找到文件: " + targetFile.getAbsolutePath());
}
// 构建访问URL
String url = buildAccessUrl(filePath);
return new FileUploadResult()
.setFileName(fileName)
.setOriginalName(originalName)
.setFileSuffix(FileUtils.getFileExtension(originalName))
.setUrl(url)
.setSize(file.getSize())
.setFilePath(filePath)
.setContentType(file.getContentType())
.setConfigKey(config.getConfigKey())
.setUploadStatus(1);
} catch (IOException e) {
log.error("本地文件上传失败: {}", e.getMessage(), e);
throw new RuntimeException("本地文件上传失败: " + e.getMessage());
}
}
@Override
public FileUploadResult upload(InputStream inputStream, String fileName, String contentType, SysOssConfig config) {
try {
// 初始化上传路径
initUploadPath();
String filePath = buildFilePath(fileName, config);
String fullPath = getNormalizedPath(uploadPath, filePath);
log.info("保存文件流到: {}", fullPath);
// 创建目录
createDirectories(fullPath);
// 保存文件
File targetFile = new File(fullPath);
try (FileOutputStream outputStream = new FileOutputStream(targetFile)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
// 获取文件大小
long size = targetFile.length();
// 构建访问URL
String url = buildAccessUrl(filePath);
return new FileUploadResult()
.setFileName(fileName)
.setOriginalName(fileName)
.setFileSuffix(FileUtils.getFileExtension(fileName))
.setUrl(url)
.setSize(size)
.setFilePath(filePath)
.setContentType(contentType)
.setConfigKey(config.getConfigKey())
.setUploadStatus(1);
} catch (IOException e) {
log.error("本地文件流上传失败: {}", e.getMessage(), e);
throw new RuntimeException("本地文件流上传失败: " + e.getMessage());
}
}
@Override
public boolean delete(String fileName, SysOssConfig config) {
try {
String filePath = config.getPrefix() + File.separator + fileName;
String fullPath = getNormalizedPath(uploadPath, filePath);
File file = new File(fullPath);
if (file.exists()) {
return file.delete();
}
return true;
} catch (Exception e) {
log.error("删除本地文件失败: {}", fileName, e);
return false;
}
}
@Override
public String getPresignedUrl(String fileName, SysOssConfig config, int expireTime) {
// 本地存储不需要预签名URL,直接返回访问URL
String filePath = config.getPrefix() + File.separator + fileName;
return buildAccessUrl(filePath);
}
@Override
public InputStream getFileInputStream(String fileName, SysOssConfig config) throws IOException {
String fullPath = getNormalizedPath(uploadPath, fileName);
log.debug("获取文件流: {}", fullPath);
File file = new File(fullPath);
if (!file.exists() || !file.isFile()) {
log.error("文件不存在: {}", fullPath);
throw new IOException("文件不存在: " + fileName);
}
return new FileInputStream(file);
}
@Override
public FilePreviewInfo getPreviewInfo(String fileName, String originalName, String contentType, long size, SysOssConfig config) throws IOException {
String fullPath = getNormalizedPath(uploadPath, fileName);
File file = new File(fullPath);
if (!file.exists() || !file.isFile()) {
log.error("文件不存在: {}", fullPath);
throw new IOException("文件不存在: " + fileName);
}
// 构建URL
String url = buildAccessUrl(fileName);
// 判断文件类型,确定预览类型
FilePreviewInfo previewInfo = new FilePreviewInfo()
.setPreviewUrl(url)
.setContentType(contentType)
.setOriginalName(originalName)
.setSize(size);
// 设置预览类型
if (isImageFile(contentType)) {
// 图片直接预览
previewInfo.setPreviewType(1);
} else if (isPdfFile(contentType)) {
// PDF直接预览
previewInfo.setPreviewType(3);
} else if (isOfficeFile(contentType)) {
// Office文档需要预览服务
previewInfo.setPreviewType(2);
previewInfo.setNeedConvert(true);
} else if (isVideoFile(contentType)) {
// 视频文件
previewInfo.setPreviewType(4);
} else if (isAudioFile(contentType)) {
// 音频文件
previewInfo.setPreviewType(5);
} else if (isTextFile(contentType, originalName)) {
// 文本文件
previewInfo.setPreviewType(6);
} else {
// 不支持预览
previewInfo.setPreviewType(0);
}
return previewInfo;
}
private String generateFileName(String originalName) {
String extension = FileUtils.getFileExtension(originalName);
String uuid = UUID.randomUUID().toString().replace("-", "");
return uuid + (StringUtils.hasText(extension) ? "." + extension : "");
}
private String buildFilePath(String fileName, SysOssConfig config) {
// 直接使用日期路径,不添加额外的前缀,避免重复的upload目录
String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
return datePath + File.separator + fileName;
}
/**
* 初始化上传根目录
*/
private void initUploadPath() throws IOException {
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
log.info("创建上传根目录: {}", uploadPath);
if (!uploadDir.mkdirs()) {
throw new IOException("无法创建上传根目录: " + uploadPath);
}
}
}
/**
* 获取规范化的完整路径
*/
private String getNormalizedPath(String basePath, String relativePath) {
// 确保基础路径不以分隔符结尾
if (basePath.endsWith(File.separator)) {
basePath = basePath.substring(0, basePath.length() - 1);
}
// 确保相对路径不以分隔符开头
if (relativePath.startsWith(File.separator)) {
relativePath = relativePath.substring(1);
}
// 组合并规范化路径
Path path = Paths.get(basePath, relativePath).normalize();
return path.toString();
}
private void createDirectories(String fullPath) throws IOException {
try {
Path path = Paths.get(fullPath).getParent();
if (path != null && !Files.exists(path)) {
log.info("创建目录: {}", path);
Files.createDirectories(path);
}
} catch (IOException e) {
log.error("创建目录失败: {}", e.getMessage(), e);
// 尝试使用File API创建目录
File directory = new File(Paths.get(fullPath).getParent().toString());
if (!directory.exists() && !directory.mkdirs()) {
throw new IOException("无法创建目录: " + directory.getAbsolutePath());
}
}
}
private String buildAccessUrl(String filePath) {
// 构建完整的访问URL
String baseUrl = "";
// 获取当前主机名和端口
baseUrl = "http://localhost:" + serverPort;
if (StringUtils.hasText(contextPath) && !contextPath.equals("/")) {
baseUrl += contextPath;
}
// 将Windows风格的路径分隔符替换为URL路径分隔符
String normalizedPath = filePath.replace(File.separator, "/");
return baseUrl + "/file/" + normalizedPath;
}
/**
* 判断是否为图片文件
*/
private boolean isImageFile(String contentType) {
return contentType != null && contentType.startsWith("image/");
}
/**
* 判断是否为PDF文件
*/
private boolean isPdfFile(String contentType) {
return contentType != null && contentType.equals("application/pdf");
}
/**
* 判断是否为Office文件
*/
private boolean isOfficeFile(String contentType) {
if (contentType == null) return false;
return contentType.contains("word") ||
contentType.contains("excel") ||
contentType.contains("powerpoint") ||
contentType.contains("ms-excel") ||
contentType.contains("ms-word") ||
contentType.contains("ms-powerpoint") ||
contentType.contains("openxmlformats");
}
/**
* 判断是否为视频文件
*/
private boolean isVideoFile(String contentType) {
return contentType != null && contentType.startsWith("video/");
}
/**
* 判断是否为音频文件
*/
private boolean isAudioFile(String contentType) {
return contentType != null && contentType.startsWith("audio/");
}
/**
* 判断是否为文本文件
*/
private boolean isTextFile(String contentType, String fileName) {
// 根据内容类型判断
if (contentType != null && (
contentType.equals("text/plain") ||
contentType.equals("text/html") ||
contentType.equals("text/css") ||
contentType.equals("text/javascript") ||
contentType.equals("application/json") ||
contentType.equals("application/xml"))) {
return true;
}
// 根据文件扩展名判断
if (fileName != null) {
String lowerName = fileName.toLowerCase();
return lowerName.endsWith(".txt") ||
lowerName.endsWith(".log") ||
lowerName.endsWith(".ini") ||
lowerName.endsWith(".json") ||
lowerName.endsWith(".xml") ||
lowerName.endsWith(".md") ||
lowerName.endsWith(".csv");
}
return false;
}
}
FileUploadStrategyFactory
就是工厂类,它负责创建和管理不同类型的 FileUploadStrategy
对象。FileUploadStrategy
是一个接口,定义了文件上传策略的通用方法,而 AliyunOssFileUploadStrategy
等实现类则是具体的策略实现。
FileUploadStrategyFactory
类通过一个 Map
来存储不同类型的 FileUploadStrategy
对象,键是策略类型(如 "aliyun"
),值是对应的策略对象。这样,当需要使用某个具体的文件上传策略时,只需要通过策略类型从工厂类中获取对应的策略对象即可,而不需要直接实例化具体的策略类。
FileUploadStrategyFactory
构造方法的含义和触发机制
@Autowired
public FileUploadStrategyFactory(List strategies) {
for (FileUploadStrategy strategy : strategies) {
strategyMap.put(strategy.getType(), strategy);
}
}
当 Spring 容器启动时,它会自动扫描所有的组件(使用 @Component
、@Service
等注解标记的类),并创建这些组件的实例。在创建 FileUploadStrategyFactory
实例时,Spring 会根据 @Autowired
注解的要求,自动查找所有实现了 FileUploadStrategy
接口的 Bean,并将它们作为一个 List
注入到 FileUploadStrategyFactory
的构造方法中。
我有三个不同的 FileUploadStrategy
实现类:AliyunOssFileUploadStrategy
、LocalFileUploadStrategy
和 MinioFileUploadStrategy
,Spring 容器会自动创建这三个类的实例,并将它们作为一个 List
注入到 FileUploadStrategyFactory
的构造方法中。然后,FileUploadStrategyFactory
会将这三个策略对象存储在 strategyMap
中,以便后续使用。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/908846.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!