diff --git a/open-isle-cli/src/components/PostEditor.vue b/open-isle-cli/src/components/PostEditor.vue index 60c7b8e7f..8b0d2255f 100644 --- a/open-isle-cli/src/components/PostEditor.vue +++ b/open-isle-cli/src/components/PostEditor.vue @@ -8,6 +8,8 @@ import { ref, onMounted, watch } from 'vue' import Vditor from 'vditor' import 'vditor/dist/index.css' +import { API_BASE_URL } from '../main' +import { getToken } from '../utils/auth' export default { name: 'PostEditor', @@ -62,6 +64,19 @@ export default { 'link', 'upload' ], + upload: { + fieldName: 'file', + url: `${API_BASE_URL}/api/upload`, + linkToImgUrl: `${API_BASE_URL}/api/upload/url`, + accept: 'image/*,video/*', + multiple: false, + headers: { Authorization: `Bearer ${getToken()}` }, + format(files, responseText) { + const res = JSON.parse(responseText) + if (res.code === 0) return res.data.url + throw new Error(res.msg || '上传失败') + } + }, toolbarConfig: { pin: true }, cache: { enable: false }, input(value) { diff --git a/src/main/java/com/openisle/controller/UploadController.java b/src/main/java/com/openisle/controller/UploadController.java new file mode 100644 index 000000000..b21c5432c --- /dev/null +++ b/src/main/java/com/openisle/controller/UploadController.java @@ -0,0 +1,77 @@ +package com.openisle.controller; + +import com.openisle.service.ImageUploader; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.util.Map; + +@RestController +@RequestMapping("/api/upload") +@RequiredArgsConstructor +public class UploadController { + private final ImageUploader imageUploader; + + @Value("${app.upload.check-type:true}") + private boolean checkImageType; + + @Value("${app.upload.max-size:5242880}") + private long maxUploadSize; + + @PostMapping + public ResponseEntity upload(@RequestParam("file") MultipartFile file) { + if (checkImageType && (file.getContentType() == null || !file.getContentType().startsWith("image/"))) { + return ResponseEntity.badRequest().body(Map.of("code", 1, "msg", "File is not an image")); + } + if (file.getSize() > maxUploadSize) { + return ResponseEntity.badRequest().body(Map.of("code", 2, "msg", "File too large")); + } + String url; + try { + url = imageUploader.upload(file.getBytes(), file.getOriginalFilename()).join(); + } catch (IOException e) { + return ResponseEntity.internalServerError().body(Map.of("code", 3, "msg", "Upload failed")); + } + return ResponseEntity.ok(Map.of( + "code", 0, + "msg", "ok", + "data", Map.of("url", url) + )); + } + + @PostMapping("/url") + public ResponseEntity uploadUrl(@RequestBody Map body) { + String link = body.get("url"); + if (link == null || link.isBlank()) { + return ResponseEntity.badRequest().body(Map.of("code", 1, "msg", "Missing url")); + } + try { + URL u = URI.create(link).toURL(); + byte[] data = u.openStream().readAllBytes(); + if (data.length > maxUploadSize) { + return ResponseEntity.badRequest().body(Map.of("code", 2, "msg", "File too large")); + } + String filename = link.substring(link.lastIndexOf('/') + 1); + String contentType = URLConnection.guessContentTypeFromStream(new ByteArrayInputStream(data)); + if (checkImageType && (contentType == null || !contentType.startsWith("image/"))) { + return ResponseEntity.badRequest().body(Map.of("code", 1, "msg", "File is not an image")); + } + String url = imageUploader.upload(data, filename).join(); + return ResponseEntity.ok(Map.of( + "code", 0, + "msg", "ok", + "data", Map.of("url", url) + )); + } catch (Exception e) { + return ResponseEntity.internalServerError().body(Map.of("code", 3, "msg", "Upload failed")); + } + } +}