c 企业网站开发,aso榜单优化,把公司建设成全国一流企业,.net 网站开发视频教程在绝大多数的项目中都会涉及到文件上传等#xff0c;下面我们来说一下技术派中是如何实现原生图片上传的#xff0c;这个功能说起来简单#xff0c;但其实对于技术还是有考验的。图片的上传涉及到IO读写#xff0c;一个文件上传的功能#xff0c;就可以把IO流涉及到的知识…在绝大多数的项目中都会涉及到文件上传等下面我们来说一下技术派中是如何实现原生图片上传的这个功能说起来简单但其实对于技术还是有考验的。图片的上传涉及到IO读写一个文件上传的功能就可以把IO流涉及到的知识点全覆盖比如字节流ByteArrayInputStream、缓存流BufferedOutputStream、文件File的读写权限、文件魔数等等。
如果你想实现一个自己的文件读写Util类需要考虑的细节还是很多的比如静态资源的配置、图片大小限制、前端图片上传组件后端图片接收参数MutiparHttpServletRequest等等。
业务介绍
技术派中关于图片上传的入口有三处 发表文章时 上传文章封面时 上传用户头像时
看发表文章时涉及到四种方式 通过编辑器的菜单添加图片。 直接复制一张图片粘贴到编辑器中 复制外部的图片链接markdown格式到编辑器中。 导入MD文件到编辑器中如果图片有连接时。 这四种方式都会出发图片上传功能严格一点后面两个还涉及到图片转链后台的接口都是一样的都调用的是ImageRestController上传图片调用的是upload方法请求参数为HttpServletRequest转存图片链接调用的是save方法参数为图片的外部链接。响应的结果为ResVoImageVo ,其中包含最关键的信息------图片路径。 代码实现 第一步在dev/application-image.yml文件中添加图片的配置
image:abs-tmp-path: /tmp/storage/web-img-path: /forum/image/tmp-upload-path: /tmp/forum/cdn-host:oss:type: localprefix: paicoding/endpoint:ak:sk:bucket:host: https://cdn.tobebetterjavaer.comspring:web:resources:# 支持本地图片上传之后的链接其中 file:///d的用于win系统后面的file: 适用于mac/linux系统static-locations:- classpath:/static/- file:///d:${image.abs-tmp-path}- file:${image.abs-tmp-path}来解释一下参数的含义 abs-tmp-path: 存储的绝对路径 web-image-path 图片在Web应用中的相对路径 tmp-upload-path 上传文件的临时存储目录 cdn-host: 图片的CDN访问域名本地不需要 oss 图片上传到阿里云OSS时的配置 spring: web: resources: static-locations 是Spring Boot提供的一种加载静态资源的机制。
静态资源通常包括CSS、JavaScript、图片等文件通过设置 spring: web: resources: static-locations我们可以告诉 Spring Boot 在哪些位置查找静态资源。 Spring Boot 的默认静态资源位置包括; 当我们为spring.web. resources. static-locations 提供自定义的值时Spring Boot会覆盖这些默认值。在技术派的项目结构中我们将CSS和javaScript以及一些图片资源放在了paicoding-ui模块static目录下。 也就意味着在我们的前端页面中如果遇到类似这样的link href/css/views/home.css relstylesheet /请求时Spring Boot将会从 classpath/static/目录下去找。 注意我们还指定了另外两个静态资源位置 file:///d:${image.abs-tmp-path} 和 file:${image.abs-tmp-path} 前者用于Windows系统 后者用于macOS和Linux系统。用macOs举例我们会把图片保存在 /tmp/storage/forum/image目录下。 也就是说我们可以通过 http//127.0.0.18080/forum/image/20230423060009676_69.jpg这种形式访问图片。 file/是一个URI统一资源标识符的方案表示在本地文件上的系统资源。例如如果你想要引用本地文件系统上的一个文件可以使用file/。以下是一些实例
file/c/path/to/your/file.txt : 表示在Windows系统上的 c/path/to/your/file.txt文件。file/Users/username/path/to/your/file.txt : 表示在macOS或Linux系统上的/Users/username/path/to/your/file.txt文件。 第二步新建ImageProperties.java类
使用 ConfigurationProperties 注解使其和配置文件中的图片配置关联起来。 Setter
Getter
Component
ConfigurationProperties(prefix image)
public class ImageProperties {/*** 存储绝对路径*/private String absTmpPath;/*** 存储相对路径*/private String webImgPath;/*** 上传文件的临时存储目录*/private String tmpUploadPath;/*** 访问图片的host*/private String cdnHost;private OssProperties oss;public String buildImgUrl(String url) {if (!url.startsWith(cdnHost)) {return cdnHost url;}return url;}
}
Setter 和 Getter 是lombok提供的注解这样我们就不用写冗长的getter 和setter。Component 表示这个类是一个 Spring管理的Bean。ConfigurationProperties是Spring Boot中用于将外部配置文件如application.properties或者application.yml中的属性绑定到Java类的一个注解。通过使用这个注解我们可以将配置文件中的值自动映射到有相应字段的Java类中。参数prefix image 表示将配置文件image作为前缀的属性绑定到该类中。 第三步构建前端上传组件和发起上传请求。 我们先来看比较简单的一种上传文章封面在发表文章的页面点击保存按钮会弹出文章封面的上传模态框。 代码非常简单用了一个input组件type为file接受的文件类型为image。 input type fileaccept image/*id uploadclass click-input
/
当选择图片后会触发change事件。 upload.on(change, function (e) {let objUrl getObjectURL(this.files[0]) //获取图片的路径该路径不是图片在本地的路径if (objUrl) {console.log(uploadImg, this.value)uploadImg(() (this.value null), objUrl)}}) 在事件回调函数中代码会执行以下操作。 使用 getObjectURL函数获取选中文件this.files[0]的临时URL。 注意这里的this指向触发事件的文件输入元素。检查objUrl是否存在。如果存在继续。输出this.value到控制台这里的this.value是选中文件的本地路径例如Windows下是 C\fakepath\file.jpg调用uploadImg函数并将一个回调函数和objUrl作为参数传递。这个回调函数将在uploadImage函数内部执行图片上传完成后。回调函数中将文件输入元素的value属性设置为null以清除选中文件。
来看一下getObjectURL函数创建一个临时URL用于访问本地文件 //建立一?可存取到?file的url
const getObjectURL function (file) {let url nullif (window.createObjectURL ! undefined) {// basicurl window.createObjectURL(file)} else if (window.URL ! undefined) {// mozilla(firefox)url window.URL.createObjectURL(file)} else if (window.webkitURL ! undefined) {// webkit or chromeurl window.webkitURL.createObjectURL(file)}return url
}这段代码使用三种不同的方式来创建临时URL以确保兼容性: window.createObjectURL(file): 这是一个比较旧的方法用于创建临时URL。在现代的浏览器中这个方法可能被废弃。
window.URL.createObjectURL(file) 这是一个比较新的方法用于创建按临时URL。在许多现代浏览器中如FIrefox、Chrome、Edge等这个方法已经取代了window.createObjectURL。window.webkitURL.createObjectURL(file) 这是一个WenKit特定的方法用于创建临时的URL。在基于WebKit的浏览器中如旧版本的Chrome和Safari这个方法可能是唯一可用的方法。
再来看uploadImge方法实用jQuery的Ajax实现图片上传 // 上传头图到服务器function uploadImg(callback, objUrl) {let uploadPic upload[0].files[0]console.log(准备上传, uploadPic)if (!checkFileSize(uploadPic)) {return;}let file new FormData()file.append(image, uploadPic)$.ajax({url: /image/upload,type: post,data: file,cache: false,contentType: false,processData: false,success: function (data) {console.log(response data, data);if (data.status.code 0) {// 图片上传失败toastr.error(data.status.msg, 图片上传失败!);return;}const {result: { imagePath },} data || {}defaults[cover] imagePath;//将图片路径存入src中显示出图片pic.attr(src, objUrl).css(visibility, visible) // 展示图片$(.upload-icon-up).css(visibility, hidden) // 隐藏上传callback();toastr.info(图片上传成功!);},error : function(jqXHR, textStatus, errorThrown) {toastr.error(jqXHR.responseText, 图片上传失败!);},})} 解释一下代码 这段代码是一个用于上传图片到服务器的函数。下面是对代码的解释
javascript // 上传头图到服务器 function uploadImg(callback, objUrl) { let uploadPic upload[0].files[0] // 获取上传的图片文件 console.log(准备上传, uploadPic) // 打印准备上传的图片信息 if (!checkFileSize(uploadPic)) { // 检查文件大小是否符合要求 return; // 如果不符合要求直接返回 } let file new FormData() // 创建一个新的FormData对象 file.append(image, uploadPic) // 将上传的图片文件添加到FormData对象中 $.ajax({ // 使用jQuery的ajax方法发送POST请求 url: /image/upload, // 请求的URL地址 type: post, // 请求类型为POST data: file, // 请求的数据为FormData对象 cache: false, // 禁用缓存 contentType: false, // 不设置Content-Type请求头 processData: false, // 不处理数据 success: function (data) { // 请求成功时的回调函数 console.log(response data, data); // 打印响应数据 if (data.status.code 0) { // 判断图片上传是否失败 // 图片上传失败 toastr.error(data.status.msg, 图片上传失败); // 显示错误提示信息 return; // 结束函数执行 } const {result: { imagePath },} data || {} // 从响应数据中提取图片路径 defaults[cover] imagePath; // 将图片路径存入defaults对象中的cover属性 // 将图片路径存入src中显示出图片 pic.attr(src, objUrl).css(visibility, visible) // 展示图片 $(.upload-icon-up).css(visibility, hidden) // 隐藏上传按钮 callback(); // 调用回调函数 toastr.info(图片上传成功); // 显示成功提示信息 }, error : function(jqXHR, textStatus, errorThrown) { // 请求失败时的回调函数 toastr.error(jqXHR.responseText, 图片上传失败); // 显示错误提示信息 }, }) }
这段代码定义了一个名为uploadImg的函数该函数接受两个参数callback和objUrl。callback是一个回调函数在图片上传成功后会被调用objUrl是图片的URL地址。
函数内部首先获取上传的图片文件并打印出准备上传的图片信息。然后通过调用checkFileSize函数来检查文件大小是否符合要求如果不符合要求则直接返回。
接下来创建一个新的FormData对象并将上传的图片文件添加到其中。然后使用jQuery的ajax方法发送POST请求将FormData对象作为请求的数据发送给服务器的/image/upload接口。
在请求成功时会打印响应数据并根据响应结果判断图片上传是否失败。如果上传失败会显示错误提示信息并结束函数执行。如果上传成功会从响应数据中提取图片路径并将其存入defaults对象的cover属性中。然后通过修改DOM元素的样式将图片路径存入src属性中并显示图片。同时隐藏上传按钮并调用传入的回调函数。最后显示成功提示信息。
在请求失败时会显示错误提示信息。 解释
函数 uploadlmg 接受两个参数:-个回调函数callback和一个对象URL objUrl。从文件输入框 upload 中获取要上传的图片文件(uploadPic)使用checkFilesize函数检査文件大小如果文件大小不符合要求则终止执行。创建一个新的FormData对象并将图片添加到其中。FormData是一个Web API它提供了一种在浏览器中方便地构造、发送表单数据的方法。它主要用于发送包含二进制文件和键值对的数据如图片、视频、文档等。FormData对象可以与Aiax一起使用以便在不刷新页面的情况下将表单数据发送到服务器。当使用FormData时浏览器会自动将数据编码为 multipart/form-data 格式这是一种特殊的格式允许在表单中包含二进制文件数据如图片或视频。使用jQuery的 $.ajax 方法发起一个异步的POST请求将图片文件发送到服务器的 /image/upload 接口。在ajax方法中设置一些关键的参数如:cache:false表示禁用浏览器缓存;contentType: false表示不设置内容类型(让浏览器自动设置);processData:false表示不对数据进行预处理。定义一个success回调函数当服务器成功响应时触发。检査响应数据中的状态(data.status.code)。如果大于0表示图片上传失败显示错误消息。否则从响应数据中获取图片路径(imagePath)将图片路径设置为pic元素的src属性使图片可见。调用回调函数callback。显示图片上传成功的提示信息定义一个error回调函数当请求发生错误时触发。在这个回调函数中显示错误消息。
第四步在ImageRestController接受图片并处理 Permission(role UserRole.LOGIN)
RequestMapping(path {image/, admin/image/, api/admin/image/,})
RestController
Slf4j
public class ImageRestController {Autowiredprivate ImageService imageService;/*** 图片上传** return*/RequestMapping(path upload)public ResVoImageVo upload(HttpServletRequest request) {ImageVo imageVo new ImageVo();try {String imagePath imageService.saveImg(request);imageVo.setImagePath(imagePath);} catch (Exception e) {log.error(save upload file error!, e);return ResVo.fail(StatusEnum.UPLOAD_PIC_FAILED);}return ResVo.ok(imageVo);} 来详细解释一下。 RequestMapping(path image/)注解用于指定控制器处理的请求路径为image。RestController 注解表示这是一个用于处理RESTful风格请求的控制器。 Slf4j 注解用于自动注入一个SLF4J日志对象使用 Autowired 注解将一个 ImageService用于处理图片保存和转链的关键类自动注入到控制器中。接下来让我们看看这个控制器中的upload方法这个方法用于处理图片上传请求。使用 RequestMapping(path upload) 注解将此方法映射到”image/upload“路径。该方法接受一个HttpServletRequest参数该参数代表客户端发送的Http请求。在该方法的内部调用imageService.saveImg(request)方法将图片保存到服务器并获取图片路径。将图片路径设置到ImageVo对象中然后将ImageVo对象作为数据返回给客户端。 第五步·定义ImageService接口
很简单不在解释。 /*** 保存图片** param request* return*/String saveImg(HttpServletRequest request);
} 第六步实现ImageService接口。 Overridepublic String saveImg(HttpServletRequest request) {MultipartFile file null;if (request instanceof MultipartHttpServletRequest) {file ((MultipartHttpServletRequest) request).getFile(image);}if (file null) {throw ExceptionUtil.of(StatusEnum.ILLEGAL_ARGUMENTS_MIXED, 缺少需要上传的图片);}// 目前只支持 jpg, png, webp 等静态图片格式String fileType validateStaticImg(file.getContentType());if (fileType null) {throw ExceptionUtil.of(StatusEnum.ILLEGAL_ARGUMENTS_MIXED, 图片只支持png,jpg,gif);}try {return imageUploader.upload(file.getInputStream(), fileType);} catch (IOException e) {log.error(Parse img from httpRequest to BufferedImage error! e:, e);throw ExceptionUtil.of(StatusEnum.UPLOAD_PIC_FAILED);}} 这个方法的主要功能是从HTTP请求中提取图片并保存描述一下该方法的逻辑 首先检查HttpServletRequest 是否是 MultipartHttpServletRequest 如果是则从请求中获取名为 “image” 的文件并将其保存到MultipartFile对象中。MultipartHttpServletRequest是一个Java接口他继承自HttpServletRequest 接口。在Spring框架中这个接口用于处理包含文件上传的HTTP请求即请求内容类型为 multipart/form-data。 在这种请求类型中表单数据可以包含文本字段和二进制文件如图片、视频、或者文档。如果file为空即未上传任何文件则抛出一个异常表示请求中缺少需要上传的图片。接下来验证图片文件类型。当前方法只支持jpg、png和webp等静态图片格式。 validateStaticImg方法检查传入的内容类型是否属于这些支持的类型并返回相应的文件类型如果文件不支持则抛出一个异常。从MultipartFile对象中获取输入流然后传递给upload方法如果在上传的过程中发生任何错误例如将图片转换为BufferedImage时出现问题将抛出一个异常。 第七步定义ImageUploader接口。
public interface ImageUploader {String DEFAULT_FILE_TYPE txt;SetMediaType STATIC_IMG_TYPE new HashSet(Arrays.asList(MediaType.ImagePng, MediaType.ImageJpg, MediaType.ImageWebp, MediaType.ImageGif));/*** 文件上传** param input* param fileType* return*/String upload(InputStream input, String fileType);/*** 获取文件类型** param input* param fileType* return*/default String getFileType(ByteArrayInputStream input, String fileType) {if (StringUtils.isNotBlank(fileType)) {return fileType;}MediaType type MediaType.typeOfMagicNum(FileReadUtil.getMagicNum(input));if (STATIC_IMG_TYPE.contains(type)) {return type.getExt();}return DEFAULT_FILE_TYPE;}
}来解释一下这段代码。 DEFAULT_FILE_TYPE 一个默认的文件类型用于表示当前文件类型无法识别时的默认值。在这个接口中他被设置为“txt”。 STATIC_IMG_TYPE 一个包含支持的静态图片类型的集合。包括PNG、JPG、WebP和GIF等类型。 String upload(InputStream input, String fileType);这是一个需要实现的抽象方法用于将输入流中的文件保存。 default String getFileType(ByteArrayInputStream input, String fileType) { 这是一个默认实现的方法用于根据输入流中的文件内容和给定的文件类型获取最终的文件类型。首先检查fileType是否为空如果为空直接返回然后使用MediaType.typeOfMagicNum(方法通过文件的魔数来判断文件类型。如果文件类型属于支持的静态图片类型集合则返回对应的扩展名。否则返回默认的文件类型。 来看一下获取文件魔数的静态方法 getMagicNum public static String getMagicNum(ByteArrayInputStream inputStream) {byte[] bytes new byte[28];inputStream.read(bytes, 0, 28);inputStream.reset();return bytesToHex(bytes);} 假如是一张jpg的文件我们来看一下魔术是多少 Testpublic void testMagic() throws FileNotFoundException {FileInputStream fileInputStream new FileInputStream(docs/imgs/init_00.jpg);ByteArrayOutputStream byteArrayOutputStream new ByteArrayOutputStream();byte[] buffer new byte[4039];int bytesRead;try {while ((bytesRead fileInputStream.read(buffer)) ! -1) {byteArrayOutputStream.write(buffer, 0, bytesRead);}} catch (IOException e) {e.printStackTrace();}ByteArrayInputStream byteArrayInputStream new ByteArrayInputStream(byteArrayOutputStream.toByteArray());//算魔数String magicNum FileReadUtil.getMagicNum(byteArrayInputStream);System.out.println(magicNum);//根据魔数判断文件类型MediaType mediaType MediaType.typeOfMagicNum(magicNum);System.out.println(文件类型 mediaType);}解释
这段代码是一个Java方法名为testMagic它没有返回值void并且声明了可能抛出FileNotFoundException异常。下面是对代码的详细解释
1. 首先创建一个FileInputStream对象fileInputStream用于读取指定路径下的文件docs/imgs/init_00.jpg。 2. 创建一个ByteArrayOutputStream对象byteArrayOutputStream用于存储从文件中读取的数据。 3. 定义一个长度为4039的字节数组buffer用于临时存储每次从文件中读取的数据。 4. 使用循环结构通过调用fileInputStream.read(buffer)方法从文件中读取数据并将读取到的字节数赋值给变量bytesRead。 5. 如果bytesRead不等于-1表示还有数据可以读取将buffer中的数据写入byteArrayOutputStream中从索引0开始写入bytesRead个字节。 6. 如果在读取文件过程中发生IOException异常捕获该异常并打印堆栈跟踪信息。 7. 创建一个ByteArrayInputStream对象byteArrayInputStream使用byteArrayOutputStream.toByteArray()方法将byteArrayOutputStream中的数据转换为字节数组作为参数传入。 8. 调用FileReadUtil.getMagicNum(byteArrayInputStream)方法获取魔数magic number并将结果赋值给字符串变量magicNum。 9. 输出魔数magicNum。 10. 调用MediaType.typeOfMagicNum(magicNum)方法根据魔数判断文件类型并将结果赋值给MediaType类型的变量mediaType。 11. 输出文件类型mediaType。
总结这段代码主要用于读取指定路径下的文件并将其内容转换为字节数组然后根据魔数判断文件类型并输出魔数和文件类型。
来看输出结果 Java字节码文件.class的魔数是一个4字节的十六进制 0xCAFEBABE。魔数是文件格式的标识符用于表示文件类型。 第八步新建LocalStorageWrapper类实现ImageUploader接口。
Slf4j
ConditionalOnExpression(value #{local.equals(environment.getProperty(image.oss.type))})
Component
public class LocalStorageWrapper implements ImageUploader {Autowiredprivate ImageProperties imageProperties;private Random random;public LocalStorageWrapper() {random new Random();}Overridepublic String upload(InputStream input, String fileType) {// 记录耗时分布StopWatchUtil stopWatchUtil StopWatchUtil.init(图片上传);try {if (fileType null) {// 根据魔数判断文件类型InputStream finalInput input;byte[] bytes stopWatchUtil.record(流转字节, () - StreamUtils.copyToByteArray(finalInput));input new ByteArrayInputStream(bytes);fileType getFileType((ByteArrayInputStream) input, fileType);}String path imageProperties.getAbsTmpPath() imageProperties.getWebImgPath();String fileName genTmpFileName();InputStream finalInput input;String finalFileType fileType;FileWriteUtil.FileInfo file stopWatchUtil.record(存储, () - FileWriteUtil.saveFileByStream(finalInput, path, fileName, finalFileType));return imageProperties.buildImgUrl(imageProperties.getWebImgPath() file.getFilename() . file.getFileType());} catch (Exception e) {log.error(Parse img from httpRequest to BufferedImage error! e:, e);throw ExceptionUtil.of(StatusEnum.UPLOAD_PIC_FAILED);} finally {log.info(图片上传耗时: {}, stopWatchUtil.prettyPrint());}}/*** 获取文件临时名称** return*/private String genTmpFileName() {return LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyyMMddhhmmssSSS)) _ random.nextInt(100);}该类使用了ConditionalOnExpression 注解表示只有在配置文件中的 image.oss.type属性为“local”时才会实例化该类。 imageProperties 一个ImageProperties 类型的对象用于获取图片相关的配置信息。 random 一个 Random类型的对象用于生成随机数。 upload(InputStream input, String fileType) 实现 ImageUploader接口中的upload方法用于将给定的输入流保存到本地的文件系统。首先检查fileType是否为null如果是则根据输入流中的字节数据的魔术确定文件的类型然后根据配置文件中的路径设置和文件类型将文件保存到本地文件并返回文件的URL。 genTmpFileName() 一个辅助方法用于生成临时文件名他使用当前日期和一个随机数生成文件名。 其中调用了 FileWriteUtil.saveFileByStream(finalInput, path, fileName, finalFileType) 方法来对图片进行保存。 public static FileInfo saveFileByStream(InputStream stream, FileInfo fileInfo) throws FileNotFoundException {if (!StringUtils.isBlank(fileInfo.getPath())) {mkDir(new File(fileInfo.getPath()));}String tempAbsFile fileInfo.getPath() / fileInfo.getFilename() . fileInfo.getFileType();BufferedOutputStream outputStream null;InputStream inputStream null;FileInfo var6;try {inputStream new BufferedInputStream(stream);outputStream new BufferedOutputStream(new FileOutputStream(tempAbsFile));int len inputStream.available();//判断长度是否大于4Kif (len 4096) {byte[] bytes new byte[len];inputStream.read(bytes);outputStream.write(bytes);} else {int byteCount false;byte[] bytes new byte[4096];//1M逐个读取int byteCount;while((byteCount inputStream.read(bytes)) ! -1) {outputStream.write(bytes, 0, byteCount);}}var6 fileInfo;return var6;} catch (Exception var16) {log.error(save stream into file error! filename: {} e: {}, tempAbsFile, var16);var6 null;} finally {try {if (outputStream ! null) {outputStream.flush();outputStream.close();}if (inputStream ! null) {inputStream.close();}} catch (IOException var15) {log.error(close stream error!, var15);}}return var6;}
该方法的参数包括输入流和一个FileInfo对象其中FileInfo对象包含了文件路径、文件名和文件类型。
代码实现了一下功能:
检查文件路径是否存在如果不存在则创建文件夹。根据文件路径、文件名和文件类型创建一个临时的绝对路径。使用BufferedInputStream和BufferedOUtputStream对输出流和输入流进行缓冲处理以提高文件的读写性能。判断输入流的长度是否大于4KB。如果小于等于4KB就一次性读取所有字节并写入输出流否则以4KB的块逐步读取输入流并写入输出流直到内容都被处理。在操作完成后返回FileInfo对象。
小结
简单总结一下本地图片上传和保存的逻辑可以分为前端和后端两个部分
前端使用Ajax上传 a.用户选择一张土图片并上传。 b.使用FormData对象封装图片数据。FormData对象能够让你通过XMLHttpRequest发送表单数据。 c. 利用jQuery的$.ajax方法发送一个POST请求将FormData 对象传递给后端的服务器。 后端Java代码处理上传和保存 从HttpServletRequest对象中提取MultipartFile对象该对象包含了上传的图片数据。 验证图片类型确保上传的文件是支持的图片格式。 将MultipartFile对象转换为InputStream以便后续处理。 调用一个专门负责处理图片上传的方法如upload该方法可能需要处理文件类型和文件名等逻辑。 保存图片到本地的文件系统。这里可以使用一个方法如 savaFileByStream将InputStream保存为文件。保存过程中可以使用BufferedInputStream和BufferedOutputStream来提高文件读写性能。 将图片的存储路径返回给前端前端可以使用这个路径来显示上传成功的图片。这样一来前端通过Ajax发送的图片数据到后端后端处理上传请求并将图片保存到本地文件系统最后将图片路径返回给前端进行展示。