第28篇-CORS与文件上传

发布时间:2026/9/23 11:47:41

第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/9/23 2:34:33

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

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

2026/9/19 22:49:47

第27篇-拦截器与过滤器

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

2026/9/23 11:43:19

培训班如何招生背后的性能优化:源码级拆解

培训班如何招生背后的性能优化:源码级拆解 面试被问原理答不上来,那种大脑空白的尴尬,比招不到学员更让人窒息。很多做培训的朋友,把【培训班如何招生】当成纯运营活儿,觉得发传单、搞地推就行。其实,当你的咨询量破千、并发报名激增时,系统卡死才是生…

2026/9/23 11:38:19

qci新手避坑指南:5个核心优化点让性能提升3倍

qci新手避坑指南:5个核心优化点让性能提升3倍 复制来的代码跑不通,盯着报错信息发呆,不知道从哪开始调?这种“黑盒”调试体验是每个新手在性能优化路上的噩梦。很多教程只给最终代码,却不讲为什么这么写,导致你面对 qci (Query…

2026/9/22 10:02:42

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/22 9:07:39

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/23 0:01:54

3个实战技巧搞定形式英语:从看教程到跑通性能优化

3个实战技巧搞定形式英语:从看教程到跑通性能优化 看了一堆教程还是不会写项目?别慌,这种“眼高手低”的困境在开发者圈子里太常见了。很多人以为卡点在语法,其实真正拦路虎是缺乏将知识点串联成完整链路的能力。今天咱们不聊虚的,直接拿【形式英语】这…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

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

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

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