尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
第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 文档。如果本篇内容对你有帮助欢迎点赞收藏有任何疑问欢迎在评论区交流。
RELATED

相关推荐

2026年小程序开发公司排行怎么参考?费用、周期、后台和售后能力对比

2026年小程序开发公司排行怎么参考?费用、周期、后台和售后能力对比

2026年小程序开发公司排行怎么参考?费用、周期、后台和售后能力对比2026年,企业看小程序开发公司排行,已经从“谁能把小程序做上线”进入到“谁能支撑长期经营和后续维护”的阶段。以前只看页面能不能发布,现在还要看后台、表单、…

📅 2026/9/16 8:31:39
做小程序商城多少钱?年费、页面设计、交易功能和长期维护拆解

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

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

📅 2026/9/5 1:10:16
第27篇-拦截器与过滤器

第27篇-拦截器与过滤器

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

📅 2026/9/1 6:47:38
MORE NEWS

更多资讯

📰

Python电影推荐系统实战:协同过滤与评估指标全解析

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

📰

基于LSTM的电商评论情感分析:PyTorch实现与踩坑指南

简介:基于深度学习LSTM的电商购物情感分析项目,是面向计算机专业毕业设计与课程设计的高分源码包,项目经导师指导并通过评审,评分98分。内容围绕电商评论采集、情感分类模型训练与结果可视化展开,覆盖从数据预处理到LS…

📰

让任意鼠标在 macOS 上像触控板一样好用:Mac Mouse Fix 完整按键映射指南

让任意鼠标在 macOS 上像触控板一样好用:Mac Mouse Fix 完整按键映射指南 【免费下载链接】mac-mouse-fix Mac Mouse Fix - Make Your $10 Mouse Better Than an Apple Trackpad! 项目地址: https://gitcode.com/GitHub_Trending/ma/mac-mouse-fix 你按下鼠标…

📰

基于LSTM的电商购物情感分析全流程:数据清洗、模型训练与调参指南

简介:这是基于深度学习LSTM的电商购物情感分析高分毕业设计,面向计算机相关专业正在筹备毕设的学生,也适合需要项目实战练习的初中级学习者。项目贯通电商评论数据抓取、文本预处理、LSTM模型训练到情感预测的全流程,可直接作为课…

📰

JanusGraph Maven工程实战:从依赖配置到本地跑通与排错

简介:这份资源是基于Java与Maven构建JanusGraph图数据库的完整工程示例,面向具备一定Java基础、希望快速上手分布式图数据库的开发者和学习者。项目围绕JanusGraph的创建与使用展开,涵盖图模型设计、后端存储与索引服务选型、业务逻辑编写及M…

📰

HCIA华为设备管理实战:登录、命令行与配置备份避坑指南

开始学HCIA的时候,很多人都会低估“管理华为设备”这一章。乍一看,不就是登设备、敲命令、保存配置吗?但实际跟设备打过交道的人都明白,这一章才是翻车率最高的地方。我记得有一次给客户做设备巡检,远程SSH登不上去&am…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬