springboot实现图片上传与下载功能
作者:会说法语的猪
这篇文章主要为大家详细介绍了后端spring项目经常要做的功能,实现图片上传和下载,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
写一下后端spring项目经常要做的功能,实现图片上传和下载,这里也把前端代码附上了。可能算是个简单版的,我这里图片上传都存在当前项目的根目录resource下了。
这里包含了,上传文件、下载文件(下载文件流、获取base64),service中还有个文件流转base64的工具方法。
下面是后端代码
Controller
package com.wft.controller; import com.wft.model.ActionResult; import com.wft.service.TestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamResource; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.*; @RestController @RequestMapping("/file") public class TestController { @Autowired TestService testService; /** * 文件上传 * @param file * @return * @throws IOException */ @PostMapping("/upload") public ActionResult uploadTest(@RequestParam("file") MultipartFile file) throws IOException { return testService.upload(file); } /** * 文件下载 * @param name * @return * @throws FileNotFoundException */ @GetMapping("/download/{name}") @CrossOrigin public ResponseEntity<InputStreamResource> downloadTest(@PathVariable("name") String name) throws FileNotFoundException { return testService.download(name); } /** * 获取文件的base64编码 * @param name * @return * @throws IOException */ @GetMapping("/getBase64/{name}") public ActionResult getBase64(@PathVariable("name") String name) throws IOException { return testService.getBase64(name); } }
Service接口就不贴了哈
ServiceImpl
package com.wft.service.impl; import com.wft.model.ActionResult; import com.wft.service.TestService; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.util.ResourceUtils; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.nio.file.Files; import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.UUID; @Service public class TestServiceImpl implements TestService { /** * 文件上传逻辑 * @param file * @return * @throws IOException */ @Override public ActionResult upload(MultipartFile file) throws IOException { if(file.isEmpty()) { return ActionResult.fail("文件不能为空"); } // 上传的文件名称 String originalFilename = file.getOriginalFilename(); // 文件后缀 String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); // 使用uuid当前文件名存储,防止名称相同覆盖 String uuid = UUID.randomUUID().toString(); String fileName = uuid + suffix; // 文件保存的路径(我这里存在当前项目下,所以获取当前项目的绝对路径,然后拼接上图片存放的文件夹的名称) String savePath = ResourceUtils.getURL("resource").getPath(); // 判断是否存在resource目录, 没有则创建 File dir = new File(savePath); if(dir != null && !dir.exists()) { dir.mkdir(); } savePath = savePath + File.separator + fileName; // 文件上传 file.transferTo(new File(savePath)); // 将文件存储的名称uuid和原文件名称返回给前端 Map<String, Object> map = new HashMap<>(); map.put("id", uuid); map.put("name", originalFilename); // 将下载图片的接口(返回文件流)路径返回给前端,前端直接将服务器地址拼上该链接即可回显图片 map.put("url", "/file/download/" + fileName); return ActionResult.success("上传成功", map); } /** * 文件下载逻辑(文件流) * @param name * @return * @throws FileNotFoundException */ @Override public ResponseEntity<InputStreamResource> download(String name) throws FileNotFoundException { String path = ResourceUtils.getURL("resource").getPath() + File.separator + name; File file = new File(path); if(!file.exists()) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } // 创建输入流 InputStream inputStream = new FileInputStream(file); // 设置HTTP头部信息 HttpHeaders headers = new HttpHeaders(); System.out.println(file.getName() + "---->>>文件名"); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getName()); // 返回文件流 ResponseEntity<InputStreamResource> body = ResponseEntity.ok() .headers(headers) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(new InputStreamResource(inputStream)); return body; } /** * 获取文件的base64编码 * @param name * @return * @throws IOException */ @Override public ActionResult getBase64(String name) throws IOException { String path = ResourceUtils.getURL("resource").getPath() + File.separator + name; File file = new File(path); if(!file.exists()) { return ActionResult.fail("文件不存在"); } byte[] fileContent = Files.readAllBytes(file.toPath()); String base64 = Base64.getEncoder().encodeToString(fileContent); base64 = "data:image/png;base64," + base64; return ActionResult.success((Object) base64); } /** * 将文件流转为base64编码(工具方法, service中接口没有该方法) * @param response * @return * @throws IOException */ public String streamToBase64(ResponseEntity<InputStreamResource> response) throws IOException { // 获取InputStreamResource对象 InputStreamResource resource = response.getBody(); if (resource == null) { throw new IllegalArgumentException("Response body is null"); } try (InputStream inputStream = resource.getInputStream(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { byteArrayOutputStream.write(buffer, 0, bytesRead); } // 将InputStream转换为字节数组 byte[] fileBytes = byteArrayOutputStream.toByteArray(); // 使用Base64编码器进行编码 String base64Encoded = Base64.getEncoder().encodeToString(fileBytes); base64Encoded = "data:image/png;base64," + base64Encoded; return base64Encoded; } } }
代码里面也都写注释了,大家一看应该就明白了。
简单说一下思路把,存图片的时候,我是以uuid当作图片的名称存储的,这样即便是前端是两个文件,但是名称一样,上传之后也不会覆盖掉原来的图片。
然后图片上传完之后,我把回显图片的路径返回给前端了,前端可以使用服务器地址(当然开发环境下会有跨域问题,一般会直接用前缀)拼上返回的这个路径即可回显图片。
返回的这个url其实就是后端编写好的一个接口,返回的是个文件流,前端直接将完整的请求的后端的路径放在img标签的src上,其实就相当于发送了请求,所以这里注意这种方式回显要求后端将改接口放在白名单中(即该接口不需要token校验),否则前端就不能像正常路径一样直接放在img的src上回显,就要像普通的接口一样调用接口,然后通过URL.createObjectURL(new Blob(res))的方式转为路径再复制给src。
然后我上面封装了了个ActionResult的返回给前端的包装类,也在这贴一下吧:
ActionResult
package com.wft.model; import lombok.Data; @Data public class ActionResult<T> { private Integer code; private String msg; private T data; public static ActionResult success() { ActionResult jsonData = new ActionResult(); jsonData.setCode(200); jsonData.setMsg("success"); return jsonData; } public static ActionResult success(String msg) { ActionResult jsonData = new ActionResult(); jsonData.setCode(200); jsonData.setMsg(msg); return jsonData; } public static ActionResult success(Object object) { ActionResult jsonData = new ActionResult(); jsonData.setData(object); jsonData.setCode(200); jsonData.setMsg("success"); return jsonData; } public static ActionResult success(String msg, Object object) { ActionResult jsonData = new ActionResult(); jsonData.setData(object); jsonData.setCode(200); jsonData.setMsg(msg); return jsonData; } public static ActionResult fail(Integer code, String message) { ActionResult jsonData = new ActionResult(); jsonData.setCode(code); jsonData.setMsg(message); return jsonData; } public static ActionResult fail(String msg, String data) { ActionResult jsonData = new ActionResult(); jsonData.setMsg(msg); jsonData.setData(data); return jsonData; } public static ActionResult fail(String msg) { ActionResult jsonData = new ActionResult(); jsonData.setMsg(msg); jsonData.setCode(400); return jsonData; } }
接下来再贴一下前端代码:
<template> <div class="wft-test"> <el-upload class="avatar-uploader" :action="baseURL + '/file/upload'" :show-file-list="false" :on-success="handleAvatarSuccess" > <img v-if="imageUrl" :src="baseURL + imageUrl" class="avatar" /> <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon> </el-upload> <!-- 测试下载图片 获取文件流 --> <el-button @click="testDownload('12b83c8d-3d54-420d-a191-bd750fa571c4.png')">测试下载图片流</el-button> <!-- 测试下载图片 获取base64 --> <el-button @click="testDownloadBase64('12b83c8d-3d54-420d-a191-bd750fa571c4.png')">测试下载图片base64</el-button> <img style="width: 200px;height: 200px;" v-if="imgBase64" :src="imgBase64" alt=""> </div> </template> <script setup lang='ts'> import { ref } from 'vue'; import request from '@/utils/request'; const imageUrl = ref(""); const imgBase64 = ref(""); // 上传成功回调 function handleAvatarSuccess(res: any) { if(res.code == 200) { imageUrl.value = res.data.url // 正常成功回显 } } /** * 测试下载图片(获取文件流) * @param fileName 文件名 */ function testDownload(fileName: string) { request({ url: `/file/download/${fileName}`, method: 'get', responseType: 'blob' }).then((res: any) => { const link = document.createElement('a') link.href = URL.createObjectURL(new Blob([res])) link.download = 'test.png' document.body.appendChild(link) link.click() document.body.removeChild(link) }) } /** * 测试下载图片(获取文件base64编码) * @param fileName */ function testDownloadBase64(fileName: string) { request({ url: `/file/getBase64/${fileName}`, method: 'get' }).then((res: any) => { if(res.code == 200) { imgBase64.value = res.data } }) } </script> <style scoped> .wft-test { width: 100%; height: 100%; } .avatar-uploader .avatar { width: 178px; height: 178px; display: block; } </style> <style> .avatar-uploader .el-upload { border: 1px dashed var(--el-border-color); border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; transition: var(--el-transition-duration-fast); } .avatar-uploader .el-upload:hover { border-color: var(--el-color-primary); } .el-icon.avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 178px; height: 178px; text-align: center; } </style>
以上就是springboot实现图片上传与下载功能的详细内容,更多关于springboot图片上传下载的资料请关注脚本之家其它相关文章!