照片展示
This commit is contained in:
@@ -8,6 +8,7 @@ public class FlashSaleSystemApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(FlashSaleSystemApplication.class, args);
|
||||
System.out.println("http://localhost:8080");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.org.flashsalesystem.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
@@ -16,6 +17,12 @@ import org.springframework.web.servlet.view.JstlView;
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Value("${flashsale.upload.path}")
|
||||
private String uploadPath;
|
||||
|
||||
@Value("${flashsale.upload.url-prefix}")
|
||||
private String urlPrefix;
|
||||
|
||||
/**
|
||||
* JSP视图解析器
|
||||
*/
|
||||
@@ -56,6 +63,10 @@ public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
registry.addResourceHandler("/favicon.ico")
|
||||
.addResourceLocations("classpath:/META-INF/resources/");
|
||||
|
||||
// 添加上传文件的静态资源映射
|
||||
registry.addResourceHandler(urlPrefix + "**")
|
||||
.addResourceLocations("file:" + uploadPath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package com.org.flashsalesystem.controller;
|
||||
|
||||
import com.org.flashsalesystem.service.AdminService;
|
||||
import com.org.flashsalesystem.service.FileUploadService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -23,6 +25,9 @@ public class AdminController {
|
||||
@Autowired
|
||||
private AdminService adminService;
|
||||
|
||||
@Autowired
|
||||
private FileUploadService fileUploadService;
|
||||
|
||||
/**
|
||||
* 获取仪表盘统计数据
|
||||
*/
|
||||
@@ -449,4 +454,38 @@ public class AdminController {
|
||||
return ResponseEntity.badRequest().body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传商品图片
|
||||
*/
|
||||
@Operation(summary = "上传商品图片")
|
||||
@PostMapping("/products/upload-image")
|
||||
public ResponseEntity<Map<String, Object>> uploadProductImage(@RequestParam("file") MultipartFile file) {
|
||||
try {
|
||||
String imageUrl = fileUploadService.uploadProductImage(file);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "图片上传成功");
|
||||
response.put("imageUrl", imageUrl);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("图片上传失败 - 参数错误", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", e.getMessage());
|
||||
|
||||
return ResponseEntity.badRequest().body(response);
|
||||
} catch (Exception e) {
|
||||
log.error("图片上传失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "图片上传失败: " + e.getMessage());
|
||||
|
||||
return ResponseEntity.badRequest().body(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
package com.org.flashsalesystem.service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 文件上传服务类
|
||||
* 处理商品图片等文件的上传
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class FileUploadService {
|
||||
|
||||
private static final String IMAGE_DIR = "products/";
|
||||
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
||||
// 允许的图片格式
|
||||
private static final String[] ALLOWED_EXTENSIONS = {
|
||||
"jpg", "jpeg", "png", "gif", "webp"
|
||||
};
|
||||
@Value("${flashsale.upload.path}")
|
||||
private String uploadPath;
|
||||
@Value("${flashsale.upload.url-prefix}")
|
||||
private String urlPrefix;
|
||||
|
||||
/**
|
||||
* 初始化上传目录
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
try {
|
||||
// 创建上传根目录
|
||||
Path rootPath = Paths.get(uploadPath);
|
||||
if (!Files.exists(rootPath)) {
|
||||
Files.createDirectories(rootPath);
|
||||
log.info("创建上传根目录: {}", uploadPath);
|
||||
}
|
||||
|
||||
// 创建商品图片目录
|
||||
Path productPath = Paths.get(uploadPath + IMAGE_DIR);
|
||||
if (!Files.exists(productPath)) {
|
||||
Files.createDirectories(productPath);
|
||||
log.info("创建商品图片目录: {}", productPath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("初始化上传目录失败", e);
|
||||
throw new RuntimeException("初始化上传目录失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传商品图片
|
||||
*
|
||||
* @param file 上传的文件
|
||||
*
|
||||
* @return 图片访问URL
|
||||
*/
|
||||
public String uploadProductImage(MultipartFile file) {
|
||||
// 检查文件是否为空
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new IllegalArgumentException("上传文件不能为空");
|
||||
}
|
||||
|
||||
// 检查文件大小
|
||||
if (file.getSize() > MAX_FILE_SIZE) {
|
||||
throw new IllegalArgumentException("文件大小不能超过10MB");
|
||||
}
|
||||
|
||||
// 获取文件扩展名
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename == null || !originalFilename.contains(".")) {
|
||||
throw new IllegalArgumentException("文件名格式不正确");
|
||||
}
|
||||
|
||||
String extension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
|
||||
|
||||
// 检查文件格式
|
||||
if (!isAllowedExtension(extension)) {
|
||||
throw new IllegalArgumentException("不支持的图片格式,仅支持: jpg, jpeg, png, gif, webp");
|
||||
}
|
||||
|
||||
try {
|
||||
// 生成新的文件名
|
||||
String newFileName = generateFileName(extension);
|
||||
|
||||
// 按日期创建子目录
|
||||
String dateDir = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
||||
String relativePath = IMAGE_DIR + dateDir + "/" + newFileName;
|
||||
|
||||
// 创建目标目录
|
||||
Path targetDir = Paths.get(uploadPath + IMAGE_DIR + dateDir);
|
||||
if (!Files.exists(targetDir)) {
|
||||
Files.createDirectories(targetDir);
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
Path targetPath = Paths.get(uploadPath + relativePath);
|
||||
file.transferTo(targetPath.toFile());
|
||||
|
||||
// 返回访问URL
|
||||
String imageUrl = urlPrefix + relativePath;
|
||||
log.info("文件上传成功: {} -> {}", originalFilename, imageUrl);
|
||||
|
||||
return imageUrl;
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("文件上传失败", e);
|
||||
throw new RuntimeException("文件上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除商品图片
|
||||
*
|
||||
* @param imageUrl 图片URL
|
||||
*/
|
||||
public void deleteProductImage(String imageUrl) {
|
||||
if (imageUrl == null || imageUrl.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 只处理本地上传的图片
|
||||
if (!imageUrl.startsWith(urlPrefix)) {
|
||||
log.warn("非本地图片,跳过删除: {}", imageUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 从URL中提取相对路径
|
||||
String relativePath = imageUrl.substring(urlPrefix.length());
|
||||
Path filePath = Paths.get(uploadPath + relativePath);
|
||||
|
||||
if (Files.exists(filePath)) {
|
||||
Files.delete(filePath);
|
||||
log.info("删除图片成功: {}", imageUrl);
|
||||
} else {
|
||||
log.warn("图片文件不存在: {}", filePath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("删除图片失败: {}", imageUrl, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一的文件名
|
||||
*
|
||||
* @param extension 文件扩展名
|
||||
*
|
||||
* @return 新文件名
|
||||
*/
|
||||
private String generateFileName(String extension) {
|
||||
return UUID.randomUUID().toString().replace("-", "") + "." + extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件扩展名是否允许
|
||||
*
|
||||
* @param extension 扩展名
|
||||
*
|
||||
* @return 是否允许
|
||||
*/
|
||||
private boolean isAllowedExtension(String extension) {
|
||||
for (String allowed : ALLOWED_EXTENSIONS) {
|
||||
if (allowed.equalsIgnoreCase(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件的MIME类型
|
||||
*
|
||||
* @param extension 文件扩展名
|
||||
*
|
||||
* @return MIME类型
|
||||
*/
|
||||
public String getMimeType(String extension) {
|
||||
switch (extension.toLowerCase()) {
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
return "image/jpeg";
|
||||
case "png":
|
||||
return "image/png";
|
||||
case "gif":
|
||||
return "image/gif";
|
||||
case "webp":
|
||||
return "image/webp";
|
||||
default:
|
||||
return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user