feat: 增强设备任务更新逻辑,支持异步图片下载和保存

主要修改:
1. 引入ImageSaveService,处理任务完成时的图片下载和保存逻辑。
2. 更新任务状态时,异步保存完成图片,确保任务状态更新与图片保存的解耦。
3. 新增saveProgressImagesForTask方法,定期保存进行中任务的图片快照。
4. 更新任务状态处理逻辑,确保即使图片保存失败,任务仍然被标记为完成。

技术细节:
- 通过异步处理,提升了任务更新的效率和用户体验。
- 新增的图片保存配置支持更灵活的图片管理和存储策略。
This commit is contained in:
zyh
2025-08-27 17:01:05 +08:00
parent 90c47df7a3
commit 01bc703ea2
5 changed files with 603 additions and 58 deletions

View File

@@ -0,0 +1,97 @@
package com.gameplatform.server.controller.image;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* 图片访问控制器
* 提供本地保存的图片访问接口
*/
@RestController
@RequestMapping("/api/images")
public class ImageController {
private static final Logger log = LoggerFactory.getLogger(ImageController.class);
private final String imageSavePath;
public ImageController(@Value("${app.image-save-path:./images}") String imageSavePath) {
this.imageSavePath = imageSavePath;
}
/**
* 获取保存的图片
* @param imagePath 图片相对路径,如 task_123_device_f1_completed_20231201_143000/homepage_1701234567890.png
* @return 图片资源
*/
@GetMapping("/{imagePath:.+}")
public ResponseEntity<Resource> getImage(@PathVariable String imagePath) {
try {
// 构建完整的文件路径
Path fullPath = Paths.get(imageSavePath).resolve(imagePath).normalize();
// 安全检查:确保请求的文件在指定目录内
Path basePath = Paths.get(imageSavePath).normalize();
if (!fullPath.startsWith(basePath)) {
log.warn("非法的图片路径访问尝试: {}", imagePath);
return ResponseEntity.notFound().build();
}
// 检查文件是否存在
if (!Files.exists(fullPath) || !Files.isRegularFile(fullPath)) {
log.debug("图片文件不存在: {}", fullPath);
return ResponseEntity.notFound().build();
}
// 创建资源
Resource resource = new FileSystemResource(fullPath);
// 设置响应头
HttpHeaders headers = new HttpHeaders();
String fileName = fullPath.getFileName().toString();
String contentType = getContentType(fileName);
headers.setContentType(MediaType.parseMediaType(contentType));
headers.setCacheControl("max-age=3600"); // 缓存1小时
log.debug("返回图片: {}", fullPath);
return ResponseEntity.ok()
.headers(headers)
.body(resource);
} catch (Exception e) {
log.error("获取图片失败: {}", imagePath, e);
return ResponseEntity.internalServerError().build();
}
}
/**
* 根据文件扩展名获取MIME类型
*/
private String getContentType(String fileName) {
String extension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
switch (extension) {
case "png":
return "image/png";
case "jpg":
case "jpeg":
return "image/jpeg";
case "gif":
return "image/gif";
case "bmp":
return "image/bmp";
default:
return "application/octet-stream";
}
}
}