保存JS中JSON或TXT或图片等到本地

本文介绍了如何利用JavaScript将JSON、TXT及图片数据保存到本地,包括引入FileSaver.js库,以及JSON和TXT文件的下载方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

保存JS中JSON或TXT或图片等到本地

引入 FileSaver.js或FileSaver.min.js

来源

GitHub
中文介绍

下载JSON

var blob = new Blob([JSON.stringify(data)], { type: "" });
saveAs(blob, "hello.json");

下载TXT

var blob = new Blob([JSON.stringify(data)], {
type: "text/plain;charset=utf-8"
});
saveAs(blob, "hello.txt");
<think>我们根据用户需求,结合站内引用,实现el-upload上传图片到Spring Boot后端,并将URL存入数据库。 用户需求:使用el-upload组件上传图片至Spring Boot后端,并将生成的图片URL保存到数据库。 步骤: 1. 前端使用el-upload组件,采用自定义上传(http-request)方式,因为用户可能需要在表单中一起提交(如引用[2]所述)。 2. 前端在自定义上传方法中,使用FormData将文件发送到后端。 3. 后端接收文件,保存到服务器(云存储),生成访问URL。 4. 后端将URL返回给前端。 5. 前端在收到URL后,可以将其存储在某个变量中,等待表单提交(如引用[2]中的editForm.imgList)者直接发送到后端保存到数据库(如用户需求)。 注意:根据引用[1],还可以在前端进行图片压缩,但用户本次问题未要求,所以先实现基本功能,压缩可以作为扩展。 具体实现: 一、前端(Vue + Element UI) 1. 模板部分 <el-upload action="#" // 不使用默认的action :auto-upload="false" // 如果手动触发上传,则设为false,这里我们使用自定义上传,所以可以不用auto-upload :show-file-list="false" :on-change="handleChange" // 当文件状态改变时,我们获取文件 :before-upload="beforeUpload" // 上传前的校验 > <el-button size="small" type="primary">点击上传</el-button> </el-upload> <el-button @click="submitUpload">提交到服务器</el-button> // 如果用户希望点击保存按钮才上传,则这里需要手动触发 但是,根据用户需求,图片上传和保存URL到数据库是两个步骤?还是同时完成?这里我们分两种情况: 情况1:用户选择图片后立即上传到服务器,然后返回URL,但此时URL还没有存到数据库,等到整个表单提交时,将URL和其他表单数据一起提交保存到数据库。 情况2:用户选择图片后立即上传到服务器,同时将URL保存到数据库(单独请求)。 根据引用[2]的描述,用户可能希望将el-upload放在表单里,点击保存才提交整个表单(包括图片URL)。所以,我们可以这样设计: 1) 用户选择图片后,先上传到服务器(此时生成URL并返回给前端),但先不保存到数据库。 2) 当用户点击整个表单的保存按钮时,将图片URL和其他表单数据一起提交,后端再统一保存到数据库。 但是,用户的问题描述是“将图片URL存在数据库中”,所以我们需要在某个时机将URL存入数据库。这里我们按照情况2的变种:在图片上传成功后,立即将URL通过另一个请求保存到数据库(这样就不需要等待整个表单提交)。但这样可能会造成多次请求,并且如果用户最终没有提交表单,就会产生无效的图片URL记录。 因此,我们按照引用[2]的思路,将图片URL先保存在前端,等到整个表单提交时,再和其他数据一起提交到后端保存到数据库。 所以,步骤调整为: 1. 用户选择图片 -> 前端校验 -> 上传到服务器(返回URL)-> 将URL保存在前端变量(比如editForm.imageUrl)中。 2. 用户填写完表单其他内容后,点击保存按钮,提交整个表单(包括editForm的所有数据,其中imageUrl就是图片的URL)到后端。 3. 后端接收到表单数据,将图片URL和其他数据一起存入数据库。 这样,我们只需要在自定义上传方法中完成文件上传并获取URL,然后赋值给表单的imageUrl字段。 二、后端(Spring Boot) 1. 文件上传接口:接收MultipartFile,保存文件,返回URL。 2. 表单提交接口:接收包含图片URL和其他字段的表单数据,存入数据库。 但是,如果用户需要同时上传多个图片,那么就需要调整。这里我们假设只上传一张图片。 具体代码实现: 前端: data() { return { editForm: { // 其他表单字段 imageUrl: '' // 用于存储图片URL }, file: null // 存储选中的文件 } }, methods: { // 文件状态改变时的回调 handleChange(file) { this.file = file.raw; // 保存文件对象 }, // 上传前的校验 beforeUpload(file) { const isImage = file.type.includes('image'); const isLt5M = file.size / 1024 / 1024 < 5; if (!isImage) { this.$message.error('只能上传图片!'); } if (!isLt5M) { this.$message.error('图片大小不能超过5MB!'); } return isImage && isLt5M; }, // 手动上传图片(在点击保存按钮时,先上传图片,再提交整个表单) async uploadFile() { if (!this.file) { // 如果没有选择图片,直接提交表单(如果有图片是必填,这里可以提示) this.submitForm(); return; } const formData = new FormData(); formData.append('file', this.file); try { const response = await axios.post('/api/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }); this.editForm.imageUrl = response.data.url; // 将返回的URL赋值给表单的imageUrl字段 // 然后提交整个表单 this.submitForm(); } catch (error) { this.$message.error('图片上传失败'); } }, // 提交表单 async submitForm() { // 将editForm提交到后端保存 try { await axios.post('/api/save', this.editForm); this.$message.success('保存成功'); } catch (error) { this.$message.error('保存失败'); } } } 但是,这样实现的话,上传图片和提交表单是连续的,如果用户先选择图片,然后填写其他表单内容,最后点击保存,那么点击保存时先上传图片(此时可能因为网络原因等待),然后再提交表单。 另一种做法:在用户选择图片后立即上传,这样用户填写表单时,图片已经在后台上传,最后提交表单时就不需要等待图片上传了。但是这样可能会产生多余的图片(如果用户最终没有提交表单,那么服务器上的图片就成了垃圾)。 因此,我们采用第一种:在保存时先上传图片,再提交表单。 注意:如果有多张图片,需要循环上传,这里不展开。 后端代码: 1. 文件上传接口(/api/upload): @RestController @RequestMapping("/api") public class FileUploadController { @Value("${file.upload-dir}") private String uploadDir; @PostMapping("/upload") public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) { try { // 创建目录 Path uploadPath = Paths.get(uploadDir); if (!Files.exists(uploadPath)) { Files.createDirectories(uploadPath); } // 生成唯一文件名 String fileName = UUID.randomUUID().toString() + "_" + file.getOriginalFilename(); Path filePath = uploadPath.resolve(fileName); // 保存文件 Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING); // 构建访问URL(这里假设通过静态资源映射可以访问到) String fileUrl = "/uploads/" + fileName; // 返回URL Map<String, String> response = new HashMap<>(); response.put("url", fileUrl); return ResponseEntity.ok(response); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传失败"); } } } 2. 静态资源映射(在application.properties中配置): file.upload-dir=uploads # 文件存储目录 spring.mvc.static-path-pattern=/uploads/** # URL路径模式 spring.web.resources.static-locations=file:${file.upload-dir} # 静态资源位置 3. 表单提交接口(/api/save): @PostMapping("/save") public ResponseEntity<?> saveFormData(@RequestBody YourFormClass formData) { // 将formData保存到数据库,其中包含图片URL // YourFormClass是一个DTO,包含imageUrl字段和其他表单字段 // 调用service保存 return ResponseEntity.ok("保存成功"); } 注意:静态资源映射的配置,这样用户可以通过http://localhost:端口号/uploads/文件名 访问到图片。 三、数据库存储 在表单提交接口中,将整个表单数据(包括图片URL)存入数据库。 四、扩展:图片压缩(根据引用[1]) 如果用户需要压缩图片,可以在前端使用canvas进行压缩,然后再上传。这里给出一个简单的压缩函数: // 压缩图片 compressImage(file, quality = 0.8) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = (event) => { const img = new Image(); img.src = event.target.result; img.onload = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 计算压缩后的宽高 let width = img.width; let height = img.height; // 设置最大宽度为800px if (width > 800) { height *= 800 / width; width = 800; } canvas.width = width; canvas.height = height; ctx.drawImage(img, 0, 0, width, height); // 转换为Blob canvas.toBlob(blob => { // 将Blob转换为File const compressedFile = new File([blob], file.name, { type: 'image/jpeg', lastModified: Date.now() }); resolve(compressedFile); }, 'image/jpeg', quality); }; }; }); } 然后在uploadFile方法中,先压缩再上传: async uploadFile() { if (!this.file) { this.submitForm(); return; } // 压缩图片 const compressedFile = await this.compressImage(this.file); const formData = new FormData(); formData.append('file', compressedFile); // 然后上传... } 五、多图片上传 如果用户需要上传多张图片,可以将editForm.imageUrl改为数组,然后在上传多张图片时,将返回的多个URL存入数组,最后提交整个数组。 六、问题:如果用户选择图片后,在保存前又换了另一张图片怎么办? 我们可以覆盖之前的文件,但之前上传的图片就会成为垃圾(因为没有被使用)。为了解决这个问题,我们可以在保存时只上传当前选中的文件,并且如果用户多次更换图片,我们只保留最后一次选中的文件。 但是,如果用户上传了图片A,然后更换为图片B,那么图片A在服务器上就没有用了。我们可以设计一个机制:在保存成功后删除未使用的图片者使用定时任务清理未关联的图片。但这样会增加复杂度。对于小规模应用,可以先不考虑。 总结:按照上述步骤,我们实现了el-upload上传图片到Spring Boot后端,并在表单提交时将图片URL和其他数据一起保存到数据库。 注意:以上代码为示例,实际应用中需要根据具体需求调整。 下面给出完整的代码示例(关键部分):</think>下面我将详细说明如何使用 `el-upload` 组件将图片上传到 Spring Boot 后端,并将生成的图片 URL 存储到数据库的完整实现方案。 ### 一、前端实现(Vue + Element UI) ```html <template> <div> <!-- 使用 http-request 自定义上传 --> <el-upload action="#" :auto-upload="false" :show-file-list="false" :on-change="handleChange" > <el-button size="small" type="primary">选择图片</el-button> </el-upload> <el-button type="success" @click="uploadImage" :disabled="!selectedFile" > 上传图片 </el-button> </div> </template> <script> import axios from 'axios'; export default { data() { return { selectedFile: null, // 存储选择的文件 imageUrl: '' // 存储返回的图片URL }; }, methods: { // 文件选择变化时触发 handleChange(file) { // 校验文件类型和大小 const isImage = /^image\/(jpeg|png|gif|webp)$/.test(file.raw.type); const isLt5M = file.raw.size / 1024 / 1024 < 5; if (!isImage) { this.$message.error('只能上传 JPG/PNG/GIF/WEBP 格式的图片!'); return false; } if (!isLt5M) { this.$message.error('图片大小不能超过 5MB!'); return false; } this.selectedFile = file.raw; return true; }, // 上传图片到后端 async uploadImage() { if (!this.selectedFile) { this.$message.warning('请先选择图片'); return; } const formData = new FormData(); formData.append('file', this.selectedFile); try { // 上传图片 const uploadResponse = await axios.post('/api/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }); this.imageUrl = uploadResponse.data.url; this.$message.success('图片上传成功!'); // 保存URL到数据库 await this.saveImageUrl(); } catch (error) { this.$message.error('上传失败: ' + error.message); } }, // 将图片URL保存到数据库 async saveImageUrl() { try { await axios.post('/api/images', { url: this.imageUrl, // 可以添加其他相关数据 // name: this.imageName, // description: this.imageDesc }); this.$message.success('图片URL已保存到数据库!'); this.selectedFile = null; // 重置选择 } catch (error) { this.$message.error('保存失败: ' + error.message); } } } }; </script> ``` ### 二、后端实现(Spring Boot) #### 1. 文件上传控制器(FileUploadController.java) ```java @RestController @RequestMapping("/api") public class FileUploadController { @Value("${file.upload-dir}") private String uploadDir; @PostMapping("/upload") public ResponseEntity<Map<String, String>> uploadFile( @RequestParam("file") MultipartFile file) { try { // 1. 创建存储目录 Path uploadPath = Paths.get(uploadDir); if (!Files.exists(uploadPath)) { Files.createDirectories(uploadPath); } // 2. 安全校验 validateFile(file); // 3. 生成唯一文件名 String originalFilename = StringUtils.cleanPath(file.getOriginalFilename()); String fileExtension = getFileExtension(originalFilename); String fileName = UUID.randomUUID().toString() + "." + fileExtension; Path filePath = uploadPath.resolve(fileName); // 4. 保存文件 Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING); // 5. 构建访问URL String fileUrl = "/uploads/" + fileName; // 6. 返回响应 Map<String, String> response = new HashMap<>(); response.put("url", fileUrl); return ResponseEntity.ok(response); } catch (IOException | IllegalArgumentException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( Collections.singletonMap("error", e.getMessage())); } } // 文件校验 private void validateFile(MultipartFile file) { // 校验文件类型 String contentType = file.getContentType(); if (contentType == null || !contentType.startsWith("image/")) { throw new IllegalArgumentException("不支持的文件类型"); } // 校验文件大小 if (file.getSize() > 5 * 1024 * 1024) { // 5MB throw new IllegalArgumentException("文件大小不能超过5MB"); } // 校验文件扩展名 String originalFilename = file.getOriginalFilename(); if (originalFilename != null) { String extension = getFileExtension(originalFilename).toLowerCase(); if (!Arrays.asList("jpg", "jpeg", "png", "gif", "webp").contains(extension)) { throw new IllegalArgumentException("不支持的文件扩展名"); } } } // 获取文件扩展名 private String getFileExtension(String filename) { return filename.substring(filename.lastIndexOf(".") + 1); } } ``` #### 2. 图片服务层(ImageService.java) ```java @Service public class ImageService { @Autowired private ImageRepository imageRepository; public Image saveImage(String imageUrl) { Image image = new Image(); image.setUrl(imageUrl); image.setCreatedAt(LocalDateTime.now()); return imageRepository.save(image); } } ``` #### 3. 图片控制器(ImageController.java) ```java @RestController @RequestMapping("/api/images") public class ImageController { @Autowired private ImageService imageService; @PostMapping public ResponseEntity<String> saveImageUrl(@RequestBody ImageRequest request) { try { imageService.saveImage(request.getUrl()); return ResponseEntity.ok("图片URL保存成功"); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("保存失败: " + e.getMessage()); } } // 请求DTO public static class ImageRequest { private String url; // Getters and Setters public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } } } ``` #### 4. 数据库实体(Image.java) ```java @Entity public class Image { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String url; @Column(name = "created_at", nullable = false) private LocalDateTime createdAt; // Getters and Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public LocalDateTime getCreatedAt() { return createdAt; } public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } } ``` #### 5. 配置文件(application.properties) ```properties # 文件存储设置 file.upload-dir=uploads spring.servlet.multipart.max-file-size=5MB spring.servlet.multipart.max-request-size=5MB # 静态资源映射 spring.mvc.static-path-pattern=/uploads/** spring.web.resources.static-locations=file:${file.upload-dir} # 数据库配置 spring.datasource.url=jdbc:mysql://localhost:3306/image_db?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=root spring.jpa.hibernate.ddl-auto=update ``` ### 三、工作流程说明 1. **前端流程**: ```mermaid sequenceDiagram 用户->>前端: 选择图片 前端->>前端: 校验图片类型和大小 用户->>前端: 点击上传按钮 前端->>后端: POST /api/upload (FormData) 后端->>后端: 保存文件到服务器 后端-->>前端: 返回图片URL 前端->>后端: POST /api/images (JSON {url}) 后端->>数据库: 保存图片URL 后端-->>前端: 返回保存结果 ``` 2. **后端流程**: - 接收上传的文件 - 校验文件类型和大小 - 生成唯一文件名并保存本地目录 - 返回可访问的图片URL - 接收图片URL并保存到数据库 ### 四、高级优化方案 1. **图片压缩(前端实现)**: ```javascript // 在handleChange方法中添加压缩逻辑 async handleChange(file) { // 压缩图片(质量75%) const compressedFile = await this.compressImage(file.raw, 0.75); this.selectedFile = compressedFile; }, // 图片压缩方法 compressImage(file, quality) { return new Promise((resolve) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = (event) => { const img = new Image(); img.src = event.target.result; img.onload = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 设置最大宽度 const MAX_WIDTH = 1920; let width = img.width; let height = img.height; if (width > MAX_WIDTH) { height *= MAX_WIDTH / width; width = MAX_WIDTH; } canvas.width = width; canvas.height = height; ctx.drawImage(img, 0, 0, width, height); // 转换为Blob canvas.toBlob( (blob) => resolve(new File([blob], file.name, { type: 'image/jpeg' })), 'image/jpeg', quality ); }; }; }); } ``` 2. **云存储集成(阿里云OSS)**: ```java // 替换FileUploadController中的保存逻辑 @Autowired private OSS ossClient; @Value("${oss.bucket-name}") private String bucketName; @Value("${oss.endpoint}") private String endpoint; public ResponseEntity<Map<String, String>> uploadFile(MultipartFile file) { try { String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename(); // 上传到OSS ossClient.putObject( bucketName, fileName, new ByteArrayInputStream(file.getBytes()) ); // 生成访问URL String fileUrl = "https://" + bucketName + "." + endpoint + "/" + fileName; Map<String, String> response = new HashMap<>(); response.put("url", fileUrl); return ResponseEntity.ok(response); } catch (Exception e) { return ResponseEntity.status(500).body(Collections.singletonMap("error", e.getMessage())); } } ``` ### 五、常见问题解决 1. **跨域问题**: ```java @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://localhost:8080") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowCredentials(true); } } ``` 2. **文件名中文乱码**: ```java // 在文件上传控制器中添加 @Bean public HttpMessageConverter<String> responseBodyConverter() { return new StringHttpMessageConverter(StandardCharsets.UTF_8); } ``` 3. **大文件上传超时**: ```properties # application.properties server.servlet.context-path=/api server.connection-timeout=60000 ``` 此方案实现了完整的图片上传流程,前端使用 `el-upload` 组件通过自定义请求上传文件,后端 Spring Boot 接收文件并保存 URL 到数据库。您可以根据实际需求选择本地存储云存储方案[^1][^2][^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值