From 2bb265d23fc2d600a9656085205be77f372b92b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=AD=9F?= <3111696955@qq.com> Date: Fri, 1 Aug 2025 17:21:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(image):=20=E4=BC=98=E5=8C=96=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E4=B8=8A=E4=BC=A0=E5=92=8C=E9=A2=84=E8=A7=88=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改图片上传接口,支持用户 ID 和 Markdown ID 参数 - 实现在线预览功能,支持多种文件类型 - 优化图片插入到 Markdown 编辑器的逻辑 - 更新数据库配置和连接字符串 --- .../controller/ImageController.java | 74 ++++++++++++++++-- .../bijihoudaun/service/ImageService.java | 2 +- .../service/impl/ImageServiceImpl.java | 5 +- .../src/main/resources/application-dev.yml | 4 +- biji-qianduan/src/api/CommonApi.js | 6 +- biji-qianduan/src/components/HomePage.vue | 10 ++- .../src/components/MarkdownEditor.vue | 15 +--- doc/111.md | 5 +- mydatabase.db | Bin 53248 -> 53248 bytes .../1e316444-2396-4f90-835b-e3a0ffe12086.png | Bin 0 -> 13472 bytes .../61af81aa-944f-4b17-9695-70482a6a5f16.png | Bin 0 -> 13846 bytes .../724d6aa4-16cf-48b6-88ba-22b33ba88b63.png | Bin 0 -> 10372 bytes .../fcd6213d-d969-4b8f-bf39-19e823857083.png | Bin 0 -> 19077 bytes 13 files changed, 91 insertions(+), 30 deletions(-) create mode 100644 uploads/1e316444-2396-4f90-835b-e3a0ffe12086.png create mode 100644 uploads/61af81aa-944f-4b17-9695-70482a6a5f16.png create mode 100644 uploads/724d6aa4-16cf-48b6-88ba-22b33ba88b63.png create mode 100644 uploads/fcd6213d-d969-4b8f-bf39-19e823857083.png diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/ImageController.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/ImageController.java index d32eda0..06c071e 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/ImageController.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/ImageController.java @@ -8,10 +8,15 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.util.List; @@ -20,19 +25,21 @@ import java.util.List; @RequestMapping("/api/images") public class ImageController { + @Value("${file.upload-dir}") + private String rootPath; + @Autowired private ImageService imageService; @Operation(summary = "上传图片") - @Parameters({ - @Parameter(name = "file", description = "图片文件", required = true) - }) @PostMapping public R uploadImage( - @RequestParam("file") MultipartFile file) { + @RequestParam("file") MultipartFile file, + @RequestParam(value = "userId", required = false) Long userId, + @RequestParam(value = "markdownId", required = false) Long markdownId) { try { - Image image = imageService.uploadImage(file); + Image image = imageService.uploadImage(file, userId, markdownId); return R.success(image); } catch (IOException e) { return R.fail(); @@ -50,6 +57,33 @@ public class ImageController { } } + /** + * 在线预览(图片、视频、音频、pdf 等浏览器可直接识别的类型) + */ + @GetMapping("/preview/{url}") + @Operation(summary = "在线预览", description = "浏览器直接打开文件流") + public void preview(@PathVariable String url, HttpServletResponse resp) throws IOException { + if (url == null) { + resp.setStatus(404); + R.fail("文件不存在"); + } + File file = new File(rootPath + File.separator + url); + if (!file.exists()) { + resp.setStatus(404); + R.fail("文件不存在"); + } + String contentTypeFromFileExtension = getContentTypeFromFileExtension(url); + // 设置正确的 MIME + resp.setContentType(contentTypeFromFileExtension); + // 设置文件长度,支持断点续传 + resp.setContentLengthLong(file.length()); + // 写出文件流 + try (FileInputStream in = new FileInputStream(file)) { + StreamUtils.copy(in, resp.getOutputStream()); + } + } + + @Operation(summary = "根据url删除图片") @PostMapping("/deleteByUrl") public R deleteImageByUrl(@RequestParam String url) { @@ -73,4 +107,34 @@ public class ImageController { } + /** + * 根据文件扩展名获取内容类型 + * @param fileName 文件名 + * @return 对应的MIME类型 + */ + private String getContentTypeFromFileExtension(String fileName) { + if (fileName == null || fileName.lastIndexOf('.') == -1) { + return "application/octet-stream"; + } + + String extension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase(); + switch (extension) { + case "jpg": + case "jpeg": + return "image/jpeg"; + case "png": + return "image/png"; + case "gif": + return "image/gif"; + case "bmp": + return "image/bmp"; + case "webp": + return "image/webp"; + case "svg": + return "image/svg+xml"; + default: + return "application/octet-stream"; + } + } + } diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/ImageService.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/ImageService.java index 89dfe0c..dad8632 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/ImageService.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/ImageService.java @@ -14,7 +14,7 @@ public interface ImageService extends IService { * @return 上传的图片对象 * @throws IOException 文件操作异常 */ - Image uploadImage(MultipartFile file) throws IOException; + Image uploadImage(MultipartFile file, Long userId, Long markdownId) throws IOException; /** * 删除图片 diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/ImageServiceImpl.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/ImageServiceImpl.java index 9bcfa7b..dbeba21 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/ImageServiceImpl.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/ImageServiceImpl.java @@ -33,7 +33,7 @@ public class ImageServiceImpl private ImageMapper imageMapper; @Override - public Image uploadImage( MultipartFile file) throws IOException { + public Image uploadImage( MultipartFile file, Long userId, Long markdownId) throws IOException { // 创建上传目录 Path uploadPath = Paths.get(uploadDir); if (!Files.exists(uploadPath)) { @@ -53,10 +53,11 @@ public class ImageServiceImpl Image image = new Image(); image.setOriginalName(originalFilename); image.setStoredName(storedName); - image.setUrl("/uploads/" + storedName); + image.setUrl("/api/images/preview/" + storedName); image.setSize(file.getSize()); image.setContentType(file.getContentType()); image.setCreatedAt(new Date()); + image.setMarkdownId(markdownId); this.save(image); return image; diff --git a/biji-houdaun/src/main/resources/application-dev.yml b/biji-houdaun/src/main/resources/application-dev.yml index 16c8b63..9f0f341 100644 --- a/biji-houdaun/src/main/resources/application-dev.yml +++ b/biji-houdaun/src/main/resources/application-dev.yml @@ -1,8 +1,8 @@ spring: datasource: driver-class-name: org.sqlite.JDBC - url: jdbc:sqlite:C:\it\houtaigunli\biji\mydatabase.db -# url: jdbc:sqlite:C:\KAIFA\2\mydatabase.db +# url: jdbc:sqlite:C:\it\houtaigunli\biji\mydatabase.db + url: jdbc:sqlite:C:\KAIFA\2\mydatabase.db jpa: hibernate: ddl-auto: none diff --git a/biji-qianduan/src/api/CommonApi.js b/biji-qianduan/src/api/CommonApi.js index 3340eb7..038d207 100644 --- a/biji-qianduan/src/api/CommonApi.js +++ b/biji-qianduan/src/api/CommonApi.js @@ -29,10 +29,12 @@ export const deleteImages = (list) => { }) } // 上传图片 -export const uploadImage = (file) => { +export const uploadImage = (file, userId, markdownId) => { const formData = new FormData() if (file) formData.append('file', file) - return axiosApi.post('/api/images?markdownId', formData, { + if (userId) formData.append('userId', userId) + if (markdownId) formData.append('markdownId', markdownId) + return axiosApi.post('/api/images', formData, { headers: { 'Content-Type': 'multipart/form-data' } diff --git a/biji-qianduan/src/components/HomePage.vue b/biji-qianduan/src/components/HomePage.vue index 89a889a..8b2fe39 100644 --- a/biji-qianduan/src/components/HomePage.vue +++ b/biji-qianduan/src/components/HomePage.vue @@ -527,14 +527,16 @@ const editNote = async (file) => { // 图片上传 const handleImageUpload=async (files) => { - const promise = await uploadImage(files); + const promise = await uploadImage(files[0]); if (promise.code !== 200) { ElMessage.error(promise.msg); return; } - const url = promise.data; - imageUrls.value.push(url); - vditor.value.insertValue(`![${files[0].name}](${url})`); + const url = promise.data.url; + // 修正IP地址并正确拼接 + const baseUrl = "http://127.0.0.1:8084"; + imageUrls.value.push(baseUrl + url); + vditor.value.insertValue(`![${files[0].name}](${baseUrl + url})`); } const debouncedSave = (value) => { diff --git a/biji-qianduan/src/components/MarkdownEditor.vue b/biji-qianduan/src/components/MarkdownEditor.vue index a51d512..5c1104d 100644 --- a/biji-qianduan/src/components/MarkdownEditor.vue +++ b/biji-qianduan/src/components/MarkdownEditor.vue @@ -21,10 +21,10 @@