第28篇-CORS与文件上传

发布时间:2026/11/13 19:22:45

第28篇-CORS与文件上传 【Kotlin Spring Boot 4 从零到架构师】第 28 篇CORS 与文件上传本系列定位零基础入门从 Kotlin 语法一路到 Spring Boot 4 高级架构DDD Modulith适合 Java 开发者转型也适合纯新手系统学习。本篇你将学到CORS 跨域问题的原因与解决方案Spring Boot 全局 CORS 配置文件上传MultipartFile文件下载ResponseEntity 流学完本篇你将能为 mini-shop 添加跨域支持和商品图片上传功能。一、CORS 跨域下面是 CORS 跨域请求的完整流程示意图后端 API(localhost:8080)前端页面(localhost:3000)浏览器后端 API(localhost:8080)前端页面(localhost:3000)浏览器浏览器检测到端口不同 → 跨域检查响应头是否允许当前源alt[允许跨域][不允许跨域]用户访问页面发送跨域请求 (GET /api/products)响应 Access-Control-Allow-Origin 头正常处理响应数据抛出 CORS 错误阻止 JS 读取1.1 什么是跨域当浏览器的前端页面和后端 API 不在同一个「源」协议域名端口时浏览器会阻止请求前端http://localhost:3000 Vite/Vue 开发服务器 后端http://localhost:8080 Spring Boot 协议相同http、域名相同localhost、端口不同3000 vs 8080 → 浏览器判定为跨域 → 默认阻止请求1.2 错误现象浏览器控制台报错Access to XMLHttpRequest at http://localhost:8080/api/products from origin http://localhost:3000 has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource.1.3 解决方案全局 CORS 配置packagecom.example.minishop.configimportorg.springframework.context.annotation.Configurationimportorg.springframework.web.servlet.config.annotation.CorsRegistryimportorg.springframework.web.servlet.config.annotation.WebMvcConfigurerConfigurationclassCorsConfig:WebMvcConfigurer{overridefunaddCorsMappings(registry:CorsRegistry){registry.addMapping(/api/**) // 对 /api/ 下所有接口生效 .allowedOrigins( // 允许的前端源 http://localhost:3000, // Vue/Vite 开发服务器 http://localhost:5173, // Vue 默认端口 http://localhost:4200 // Angular 默认端口 ) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) // 允许所有请求头 .allowCredentials(true) // 允许携带 Cookie .maxAge(3600) // 预检请求缓存 1 小时 } }1.4 生产环境 CORSConfigurationclassCorsConfig:WebMvcConfigurer{overridefunaddCorsMappings(registry:CorsRegistry){registry.addMapping(/api/**) // 生产环境只允许前端域名 .allowedOrigins( https://www.mini-shop.com, https://mini-shop.com ) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600) } }安全提示不要在生产环境用allowedOrigins(*)allowCredentials(true)这会被浏览器拒绝。如果允许任意域名用allowedOriginPatterns(*)。1.5 单个接口的 CORS如果只有个别接口需要跨域RestControllerRequestMapping(/api/products)CrossOrigin(origins[http://localhost:3000])// 类级别classProductController{GetMapping(/{id})CrossOrigin(origins[http://localhost:5173])// 方法级别fungetById(PathVariableid:Long):Product{...}}二、文件上传下面是文件上传的完整处理流程图是否否是客户端发起上传请求文件是否为空抛出 BusinessRuleException文件不能为空文件类型是否允许抛出 BusinessRuleException不支持的文件类型生成唯一文件名UUID 原始扩展名创建目标目录Files.createDirectories()写入文件到磁盘Files.copy()返回文件信息文件名/大小/URL前端获取 URL展示上传结果2.1 配置上传限制spring:servlet:multipart:enabled:truemax-file-size:10MB# 单个文件最大 10MBmax-request-size:50MB# 单次请求最大 50MB2.2 文件上传接口packagecom.example.minishop.controllerimportcom.example.minishop.exception.BusinessRuleExceptionimportcom.example.minishop.dto.ApiResponseimportorg.springframework.beans.factory.annotation.Valueimportorg.springframework.web.bind.annotation.*importorg.springframework.web.multipart.MultipartFileimportjava.nio.file.Filesimportjava.nio.file.Pathimportjava.nio.file.Pathsimportjava.nio.file.StandardCopyOptionimportjava.util.UUIDRestControllerRequestMapping(/api/files)classFileController(// 从配置文件读取上传目录Value(\${mini-shop.upload-dir:uploads})privatevaluploadDir:String){PostMapping(/upload)funupload(RequestParam(file)file:MultipartFile,RequestParam(defaultValueproduct)type:String):ApiResponseMapString,String{// 1. 校验文件if(file.isEmpty){throwBusinessRuleException(文件不能为空)}valoriginalFilenamefile.originalFilename?:throwBusinessRuleException(文件名不能为空)// 2. 校验文件类型valallowedExtensionssetOf(jpg,jpeg,png,gif,webp)valextensionoriginalFilename.substringAfterLast(.,).lowercase()if(extension!inallowedExtensions){throwBusinessRuleException(不支持的文件类型.$extension)}// 3. 生成唯一文件名防止覆盖valnewFilename${type}/${UUID.randomUUID()}.$extension// 4. 创建目录并保存valtargetPath:PathPaths.get(uploadDir,newFilename)Files.createDirectories(targetPath.parent)// 5. 写入文件file.inputStream.use{input-Files.copy(input,targetPath,StandardCopyOption.REPLACE_EXISTING)}// 6. 返回文件信息returnApiResponse.success(mapOf(filenametonewFilename,originalNametooriginalFilename,sizetofile.size.toString(),urlto/uploads/$newFilename))}}2.3 上传测试curl-XPOST http://localhost:8080/api/files/upload\-Ffilekeyboard.jpg\-Ftypeproduct响应{code:200,message:success,data:{filename:product/550e8400-e29b-41d4-a716-446655440000.jpg,originalName:keyboard.jpg,size:245678,url:/uploads/product/550e8400-e29b-41d4-a716-446655440000.jpg}}2.4 静态资源映射上传的文件需要能被访问到配置静态资源映射ConfigurationclassWebConfig:WebMvcConfigurer{overridefunaddResourceHandlers(registry:ResourceHandlerRegistry){// 把 /uploads/** URL 映射到本地文件目录registry.addResourceHandler(/uploads/**) .addResourceLocations(file:uploads/) } }三、文件下载下面是文件下载的流程示意图否是客户端请求下载/download/{filename}文件是否存在抛出 ResourceNotFoundException探测文件 ContentType设置响应头Content-Disposition: attachmentFiles.copy()写入响应输出流浏览器触发下载GetMapping(/download/{filename})fundownload(PathVariablefilename:String,response:HttpServletResponse){valfilePath:PathPaths.get(uploadDir,filename)if(!Files.exists(filePath)){throwResourceNotFoundException(文件,filename)}valcontentTypeFiles.probeContentType(filePath)?:application/octet-streamresponse.contentTypecontentType response.setHeader(Content-Disposition,attachment; filename\$filename\)Files.copy(filePath,response.outputStream)}本篇小结知识点核心内容CORS浏览器安全策略阻止跨源请求全局 CORS 配置WebMvcConfigurer.addCorsMappings()CrossOrigin单个接口的跨域配置allowedOrigins允许的前端域名allowCredentials允许携带 Cookie文件上传RequestParam(file) MultipartFile上传限制spring.servlet.multipart.max-file-size文件名防覆盖UUID 生成唯一文件名文件下载Files.copy(path, response.outputStream)静态资源映射addResourceHandler(/uploads/**)下篇预告第 29 篇SpringDoc OpenAPI 3 — API 文档一行代码不写就能生成漂亮的 API 文档下一篇集成 SpringDoc为 mini-shop 生成交互式 API 文档。如果本篇内容对你有帮助欢迎点赞收藏有任何疑问欢迎在评论区交流。
延伸阅读

更多相关文章

2026/11/13 9:59:23

做小程序商城多少钱?年费、页面设计、交易功能和长期维护拆解

做小程序商城多少钱?年费、页面设计、交易功能和长期维护拆解企业问做小程序商城多少钱,已经不能只看一个首年报价。商城费用要拆成年费版本、页面定制设计、交易功能、支付配置、会员营销、配送/自提/核销、售后培训、续费维护和可能的定制开发成本。很…

2026/11/13 20:46:00

第27篇-拦截器与过滤器

【Kotlin Spring Boot 4 从零到架构师】第 27 篇:拦截器与过滤器 本系列定位:零基础入门,从 Kotlin 语法一路到 Spring Boot 4 高级架构(DDD Modulith),适合 Java 开发者转型,也适合纯新手系统…

2026/11/14 4:22:13

高压电工证与低压电工证的区别在哪考才靠谱

高压电工证与低压电工证的区别在哪考才靠谱 你是不是正愁找不到电工工作,因为 急需特种作业证才能上岗找工作 ?别急,今天就来聊聊 高压电工证与低压电工证的区别 ,以及你最关心的问题—— 在哪考 ,让你少走弯路,快速拿到证书,顺利上岗。…

2026/11/14 4:22:13

广州花都怎么考电工证 值不值得考?证书过期了不知道怎么复审延期

广州花都怎么考电工证 值不值得考?证书过期了不知道怎么复审延期 证书过期了不知道怎么复审延期,这事儿我见过太多人踩坑。尤其在像广州花都这样的地方,电工证是很多建筑、制造业从业人员的“刚需”,一旦过期,不仅影响工作,还可能被单位辞退。很多人在…

2026/11/14 4:22:13

许昌电工证办理学校值不值得考?实操考试心里没底怕挂科怎么办

许昌电工证办理学校值不值得考?实操考试心里没底怕挂科怎么办 实操考试心里没底怕挂科,这是很多准备报考【许昌电工证办理学校】的学员最真实的感受。尤其是在特种作业领域,证书不仅是上岗的门槛,更是安全作业的“护身符”。但很多学员在选择是否报考时,…

2026/11/13 22:33:32

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/14 0:06:39

土木特种工考证官方报名入口怎么找 之前考的证在外省能不能转过来

土木特种工考证官方报名入口怎么找 之前考的证在外省能不能转过来 之前考的证在外省能不能转过来?这个问题我太熟悉了,作为一名干了十几年的特种作业证书办理从业者,每年都会遇到不少像你一样在外地考了证,回来却发现无法直接使用的学员。别急,今天我就…

2026/11/14 0:06:39

电工证要多久多钱保姆级教程:工地太忙根本没时间复习考试

电工证要多久多钱保姆级教程:工地太忙根本没时间复习考试 工地太忙根本没时间复习考试,你不是一个人。在信阳浉河区,很多刚进入工地的电工都面临这样的困境:工作强度大,时间碎片化,根本没有系统复习考试的机会。但电工证是上岗的硬门槛,…

2026/11/13 22:32:11

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/13 12:14:39

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑