IIS下C#大文件上传配置全攻略:解决413与404.13错误 给大文件上传加配置这事儿说难不难但说简单也真不简单。你写好了C#后端接口也测通了结果部署到IIS上一传大文件就报错不是413就是404.13甚至直接给你来个连接重置。这时候很多人第一反应是“代码有问题”其实大多数情况根本不是代码的锅而是IIS和ASP.NET的双层限制没打通。这篇文章我就从头到尾把IIS下上传大文件的配置逻辑讲透包含配置节点、实操步骤、C#后端配合写法以及我这些年踩过的坑。1. 先搞懂IIS限制大文件的底层逻辑1.1 请求限制其实有“两道关口”很多人以为IIS限制上传大小就一个配置项其实完整链路里有两道关口分别管不同的层面。第一道关口是IIS本身的请求筛选模块Request Filtering它工作在ASP.NET之前属于Web服务器层。当HTTP请求还在IIS手里时它会检查请求的某些特征比如URL长度、查询字符串长度、请求内容长度Content-Length。一旦超过预设值IIS会直接返回404.13或413.1之类的错误你的C#代码压根不会被执行。第二道关口是ASP.NET运行时本身。请求通过了IIS的筛选后进入ASP.NET管线这里还有一个maxRequestLength限制。如果你用的是ASP.NET Framework 4.x这个限制由httpRuntime节点的maxRequestLength属性控制如果你用的是ASP.NET Core托管在IIS上情况又不太一样后面我会专门说。想理解为什么是两个限制可以和小区门禁做个类比小区大门是第一道门禁楼栋单元门是第二道。你光打开了单元门的门禁但大门那边没放行人还是进不来。反过来也一样。很多人在web.config里只改了一处另一个地方还在用默认值自然就传不上大文件。1.2 默认值到底是多少了解默认值很重要不然你都不知道自己踩的是哪个限制。默认情况下IIS请求筛选的maxAllowedContentLength默认是30MB也就是30000000字节。ASP.NET Framework的httpRuntime.maxRequestLength默认是4096KB也就是4MB。这里有个特别容易误导人的地方如果你只是普通上传几MB的小文件4MB这个默认值可能就够用了但一旦超过4MB即使IIS那边放行了ASP.NET也会直接抛异常。反过来如果IIS那边先拦住了那ASP.NET的错误都不会出现。很多新手只改了一个地方调试半天找不到原因就是因为没意识到这条链上有两道门。另外还要注意IIS的错误页面里如果出现404.13说明是请求筛选模块拦截的错误信息里通常写着“内容长度太长”。如果出现的是413 Request Entity Too Large那可能是IIS层面或Web.config里某些特殊配置导致的这两类错误的排查方向完全不同。1.3 不同ASP.NET版本的配置差异这里必须分开说因为很多人用Framework 4.x但也有不少项目已经迁移到了ASP.NET Core两者的配置思路差异很大。Framework 4.x时代配置主要集中在web.config里包括system.web下的httpRuntime节点和system.webServer下的security节点。IIS管理器里也可以直接改但说实话IIS管理器改完底层改的还是这两个节点的值。ASP.NET Core时代如果你使用的是进程内托管InProcess那么请求先经过IIS再由IIS转发给应用程序。IIS侧的maxAllowedContentLength限制仍然生效但ASP.NET Core自身不再读取httpRuntime.maxRequestLength而是由Kestrel或IIS的中间件来管控。所以在Core项目里你主要得搞定两层IIS请求筛选限制和Kestrel的请求体大小限制。有个细节容易被忽略ASP.NET Core的InProcess托管下IIS会把请求转发给Kestrel实际上走了两个HTTP服务器所以两边的限制都要放宽。2. 实操按两种方式配置IIS大文件上传2.1 方式一直接改IIS管理器的请求筛选设置如果你习惯用图形界面操作这种方式的优点是直观修改完立刻生效不需要停站点。具体步骤是这样打开IIS管理器找到你的站点双击“请求筛选”功能模块。如果没有看到这个图标确认一下IIS是否安装了相应的功能模块某些精简安装的IIS缺了“请求筛选”模块需要去“启用或关闭Windows功能”里勾上。进入请求筛选界面后点击右侧操作区的“编辑功能设置”会弹出一个小窗口。窗口底部有一个“最大允许内容长度(字节)”的输入框直接把默认的30000000改成你需要的数值比如104857600也就是100MB。确定保存后这个限制就改好了。需要注意一点这里的单位是字节不是KB也不是MB。很多人在这里填100以为就是100MB结果IIS直接把所有超过100字节的请求全拦了闹出笑话。换算一定要算清楚实在懒得算可以用1024 * 1024 * 100这样的乘法表达式在计算器里算好再填。如果你的站点配置了多个不同站点要注意每个站点是独立的还是继承服务器级别的。请求筛选既可以在服务器级别配置也可以在站点级别配置站点级别会覆盖服务器级别的设置。我给客户排查问题时经常发现他们在服务器级别改了但某个站点单独设置了更小的值结果那个站点还是传不了大文件。2.2 方式二通过web.config直接配置节点图形界面方便是方便但做项目交付的时候最好还是把配置写进web.config里这样跟着代码走到了新环境部署就不用重新手点一遍。Framework 4.x项目的web.config需要加上或修改以下配置configuration system.web !-- maxRequestLength 单位是KB默认4096KB即4MB这里设置为100MB -- httpRuntime targetFramework4.8 maxRequestLength102400 executionTimeout300 / /system.web system.webServer security requestFiltering !-- maxAllowedContentLength 单位是字节默认30000000即30MB这里设置为100MB -- requestLimits maxAllowedContentLength104857600 / /requestFiltering /security /system.webServer /configurationexecutionTimeout的单位是秒我这里设置了300秒也就是5分钟。上传大文件时如果后端处理逻辑复杂比如还有转码、压缩之类的操作执行时间很容易超过默认的110秒然后请求就被掐断了。这个值要根据你的业务场景灵活调整不是一个通用的定值。对于ASP.NET Core项目Kestrel自身的限制也要放开。你可以在Program.cs里这样配置builder.WebHost.ConfigureKestrel(options { options.Limits.MaxRequestBodySize 100 * 1024 * 1024; });同时IIS侧仍然要配置requestFiltering的maxAllowedContentLength。另外如果你在IIS上启用了ASP.NET Core Module V2并且遇到请求体过大的问题还需要检查一下web.config里aspNetCore节点的requestTimeout属性是否够长。2.3 补充配置超时与动态内容限制还有一个很多人会漏掉的地方IIS里的“CGI限制”或“FastCGI限制”。如果你的应用是通过IIS FastCGI承载的那还需要检查FastCGI设置里的活动超时和请求超时参数。对C#项目来说Framework 4.x走的是托管管道通常不涉及FastCGI但如果你看到自己的站点是用FastCGI方式跑的比如某些PHP转过来的混用环境就要注意这一层。另一个经常被忽略的点是IIS的Dynamic Content Compression动态内容压缩。上传接口一般不受影响但如果上传后紧接着要下载或预览某些压缩设置可能导致大文件处理异常。这个不属于上传配置的必改项但排查问题的时候可以作为一个怀疑方向。3. C#后端处理大文件的注意事项3.1 不要用Request.Files直接SaveAs大文件我这里得强调一个很多教程没有讲透的坑用Request.Files接收大文件尤其是几十MB上百MB的时候默认情况下HttpFileCollection会把文件缓冲到内存然后才允许你SaveAs。这意味着服务器内存占用会飙升并发一高IIS进程池的内存占用率直接冲到90%以上接着就是进程回收所有未完成的请求全部中断。我在实际项目中遇到过一种典型场景客户上传100MB的视频文件单次上传一两个还能接受但一旦有3个人同时传服务器直接假死。最后检查下来就是Request.Files这个默认缓冲行为惹的祸。想规避这个问题更好的做法是用Request.GetBufferlessInputStream()或者Request.GetBufferedInputStream()配合封装好的分块读取逻辑。GetBufferlessInputStream的意思是“不经过令牌的请求体缓冲”它让你可以自己控制读取的速度和方式而不是让IIS自动把整段请求体全部读进内存。这个思路实现的代码可以这样写public async TaskIActionResult Upload() { var stream Request.Body; var filePath Path.Combine(_uploadPath, Guid.NewGuid().ToString(N)); using (var fileStream new FileStream(filePath, FileMode.Create, FileAccess.Write)) { var buffer new byte[81920]; int bytesRead; while ((bytesRead await stream.ReadAsync(buffer, 0, buffer.Length)) 0) { await fileStream.WriteAsync(buffer, 0, bytesRead); } } return Ok(); }当然这段代码在Framework 4.x里对应的是Request.GetBufferlessInputStream()在ASP.NET Core里直接用Request.Body就行。别小看这个差别很多从老项目迁到Core的朋友在这上面栽过跟头。3.2 尽量用异步而不是同步处理上传大文件本身就是I/O密集型的操作如果用同步方式处理线程池线程会被长时间占用。线程池一旦耗尽其他小请求也进不来整个站点表现就像“死了”一样。这不是上传功能本身的问题而是处理方式没有为高并发场景留余地。所以在写上传接口时尽量用async TaskIActionResult这种异步方式。C#的异步不是性能加速器它的核心价值是“不阻塞线程”让线程在处理大文件I/O等待期间可以回去服务别的请求这对服务器的整体吞吐量提升非常明显。3.3 生成文件名与路径规划大文件上传的另一个隐藏问题是磁盘空间和文件名冲突。我见过有人的代码直接用了Request.Files[0].FileName作为服务器端的文件名结果导致两个隐患一是用户上传的文件名可能包含中文或特殊字符Windows和Linux下的表现不一致二是多个人上传同名文件时互相覆盖。更稳妥的做法是服务端生成不重复的文件名比如用Guid.NewGuid().ToString(N)然后把原始文件名通过另一个字段比如fileName参数传给后端做映射或者保存时把原始文件名存入数据库。磁盘空间的管理也要提前规划最好按日期分目录存放。如果同一目录下文件数量超过几千Windows文件系统的枚举性能会明显下降。这不是IIS层面的问题但出现性能问题时往往会被误报为“IIS上传配置有问题”。4. 前端配合分片上传与进度条4.1 为什么只调后端配置还不够就算IIS和ASP.NET都调好了直接用传统form提交超大文件浏览器层面也有各种问题。比如网络不稳定导致上传中断整个文件就需要重新上传或者浏览器内存占用过高导致页面卡顿。这里最实用也最常见的方案是“分片上传”前端把大文件切成若干个几MB大小的块然后逐个上传最后后端合并。分片上传的好处很明显每片都比较小单次请求不会触发IIS和ASP.NET的大文件限制即使后端配置还没调小分片也能顺利传上去。失败重传的成本大大降低哪个分片失败了就重传哪个分片。可以天然获得上传进度已经上传完的分片计数除以总分片数就是进度百分比。4.2 前端分片与后端合并的实现思路前端用File对象、Blob.prototype.slice方法对文件分片用FormData把分片数据发给后端。核心伪代码如下const file document.getElementById(fileInput).files[0]; const CHUNK_SIZE 5 * 1024 * 1024; // 每片5MB const totalChunks Math.ceil(file.size / CHUNK_SIZE); for (let i 0; i totalChunks; i) { const start i * CHUNK_SIZE; const end Math.min(start CHUNK_SIZE, file.size); const chunk file.slice(start, end); const formData new FormData(); formData.append(chunkIndex, i); formData.append(totalChunks, totalChunks); formData.append(fileId, fileId); formData.append(file, chunk); console.log(正在上传第${i 1}片共${totalChunks}片); await fetch(/api/upload/chunk, { method: POST, body: formData }); }后端对应接收每个分片把数据追加到指定文件里。如果一个请求对应一个分片每个分片只有5MB那后端配置的压力就小很多。我个人的经验是即便IIS配置已调大还是建议配合分片上传一起做。两者并不冲突分片上传更强的其实是容错能力。4.3 合并时机与完整性校验分片上传最让人头疼的是“什么时候合并”和“怎么保证文件没传坏”。合并时机可以用一个状态表来管理比如在数据库中记录文件IDfileId、总分片数、已上传分片索引。每次收到一个分片就把对应的索引记为已完成当已完成的索引数量等于总分片数时触发合并动作。合并本身很简单按索引顺序把所有分片写入同一个文件。但这里有个坑如果分片到达的顺序是乱的直接从头到尾写就会出错所以合并的代码里一定要根据chunkIndex先对分片排序再顺序合并。至于完整性校验最常用的方式是前端计算整个文件的MD5后端在合并完成后用同一种算法计算合并后文件的MD5两者一致才算上传成功。大型文件计算MD5本身也耗时所以可以把校验动作提前到每个分片各自算MD5后端每收到一片就校验一片的MD5是否匹配不匹配的请求直接丢弃并返回错误码前端发现后重传这个分片。这样能马上发现传输损坏而不是等到最后合并完了才发现文件打不开省很多事。5. 常见问题与排查技巧实录5.1 不同错误码意味着什么上传大文件时最常见的错误码就那么几个很多人在网上搜索半天其实对照一下自己的错误码就能快速定位问题错误码含义主要原因排查方向404.13内容长度过大被拒绝请求筛选的maxAllowedContentLength超限检查IIS请求筛选设置413 Request Entity Too Large请求实体过大IIS或ASP.NET层限制检查httpRuntime和requestFiltering两边413.1请求内容长度被限制IIS请求筛选模块拦截调整maxAllowedContentLength500 内部服务器错误后端处理出错各种原因需看日志开启详细错误日志连接被重置连接中断超时、进程回收检查executionTimeout和进程回收设置我发现一个高频场景用户把maxAllowedContentLength改大了但httpRuntime.maxRequestLength没改。由于后者默认只有4MB其表现就是小文件能传超过4MB就报500。这种情况下错误信息不一定直白建议先在浏览器F12里看网络响应再从IIS日志和事件查看器里找细节。5.2 配置改了仍然无效的排查思路这种问题我也被问过很多次说“我明明改了web.config重启了站点为什么还是传不了大文件”。我最常先问三个问题改的是不是当前站点对应的web.config有没有真正重启应用程序池错误码到底变了没有第一点很常见站点下有子应用子应用的web.config覆盖了父级的配置。第二点也很常见服务器装了WAF或防火墙模块它可以在IIS层面二次拦截请求web.config只管得到IIS管不住防火墙。第三点很多人忽略修改web.config后如果不回收应用程序池生效时间不确定。IIS会监控web.config的变化并自动触发应用程序池回收但有时监控延迟或者你改错位置了错误码还是一样你以为没生效其实已经没有真正触发。另外如果你的环境前面有反向代理服务器比如Nginx或HAProxy还需要检查代理服务器的client_max_body_size之类的配置。反向代理会先于IIS接收请求如果代理层没放开IIS怎么配都无用。这类问题在本地调试时一切正常部署到测试环境后突然传不了大文件首先怀疑中间链路。5.3 大文件上传导致内存或磁盘占满怎么办即使配置全调好了大文件上传依然可能引发服务器资源问题。最容易出现的两类一是进程池内存暴涨尤其是用了Request.Files的情况下二是磁盘被撑爆尤其没做定期清理的服务器。进程内存问题我建议的方案是把最大请求体限制调到一个合理范围同时后端采用流式读取而不是整体缓冲。IIS进程池也建议限制内存上限比如设成1GB当超过这个上限时自动回收避免一个上传任务把整个服务器拖垮。磁盘问题建议在应用层面做定时任务定期清理几天前的临时分片、上传未完成留下的残留文件、以及超过保留期限的正式文件。这个不只是节省空间也是防止某些用户恶意多次上传导致磁盘被快速塞满。还有杀毒软件的问题。Windows Server上安装杀毒或安全软件时如果它对上传目录做实时扫描大文件写入会变得异常缓慢甚至导致文件被锁。排查方法很简单把上传目录加入杀毒软件的排除列表然后重新测试上传对比耗时差异就清楚了。5.4 前端超时、浏览器拦截与网络波动后端配置好以后前端也需要同步调整。浏览器默认没有“上传超时”这一说但如果你用了axios、fetch之类的库它们可能默认有超时设置。axios的默认超时是0不超时但如果你显式设置了timeout上传大文件时就很容易触顶。比较稳妥的做法是上传接口不设置固定超时或者设置一个很长的时间比如30分钟同时配合分片上传让单个请求的耗时控制在十几秒内。这样即使网络波动用户也只需要重传当前分片不是整个文件。网络波动导致的连接重置通常表现为上传到一半进度条卡住然后失败。分片上传可以自动重试失败的分片比如捕获异常后延迟1秒重新发起当前分片连续失败3次才提示用户。这种体验已经接近“断点续传”的效果了实际项目里很管用。6. 进阶实践与个人经验小结6.1 上传服务分离出去的架构考量如果项目规模比较大、上传需求频繁我更建议把“接收上传文件”这一职责拆成一个独立的服务不要和业务接口挤在同一个Web应用里。原因很实际大文件上传占用带宽和磁盘I/O如果和其他API混在一起上传高峰期会把其他接口的响应速度拖慢。独立上传服务可以是另一个站点或应用程序池物理隔离掉大文件I/O对主业务的影响。磁盘也可以用独立的分区避免主系统盘被上传文件塞满。这种架构方式不复杂就是多用一台服务器或者一个站点池但维护成本会低很多。还有一个容易被忽略的细节上传文件的服务和后续下载文件的服务尽量用同一个域名体系避免跨域导致的额外配置。如果你非要把上传放在upload.xxx.com、下载放在file.xxx.com那么跨域配置、Cookie策略都得跟着改前端也会多出不少工作。6.2 测试大文件上传时要注意的模拟方法测试大文件上传很多人会顺手找一个几GB的视频素材直接拖上去测。这样测确实真实但效率太低而且不一定能精准复现问题。其实可以用命令行快速生成一个指定大小的文件用来测试配置是否生效。Windows下可以用以下命令生成一个500MB的空文件fsutil file createnew C:\temp\large_upload_test.bin 524288000Linux下则用dd命令dd if/dev/zero oflarge_upload_test.bin bs1M count500用这种方式可以精准控制文件大小方便验证100MB、200MB、2GB不同档位的配置边界。还有一个更好的思路先测一个略低于配置上限的文件再测一个略超上限的文件对比两次结果能精准判断是不是限制拦截。6.3 配置模板直接抄如果你需要一套可以直接复制粘贴的配置模板我整理了一份个人项目里常用的大文件上传配置。Framework 4.x项目的web.config核心配置configuration system.web httpRuntime maxRequestLength2097152 executionTimeout1800 / /system.web system.webServer security requestFiltering requestLimits maxAllowedContentLength2147483648 / /requestFiltering /security handlers remove nameExtensionlessUrlHandler-Integrated-4.0 / remove nameOPTIONSVerbHandler / remove nameTRACEVerbHandler / add nameExtensionlessUrlHandler-Integrated-4.0 path*. verb* typeSystem.Web.Handlers.TransferRequestHandler preConditionintegratedMode,runtimeVersionv4.0 / /handlers /system.webServer /configuration注意这里maxRequestLength单位是KB2097152KB换算过来是2GB。maxAllowedContentLength单位是字节2147483648正好是2GB的上限。实际项目里通常不需要开这么大几百MB已经足够这个值要根据业务文件大小来定。ASP.NET Core项目的appsettings.json或Program.cs里配置builder.WebHost.ConfigureKestrel(options { options.Limits.MaxRequestBodySize 2L * 1024 * 1024 * 1024; // 2GB });同时不要忘了IIS侧的requestFiltering配置。6.4 我个人实操下来的四点体会配置大文件上传这件事表面上一两个节点就能搞定但实际落地总会冒出各种环境差异。我自己的经验是注意下面几点第一web.config改动后IIS会自动重启应用程序池但这个“自动”不一定立马发生。如果改了配置测试还是老样子手动回收一下应用程序池再试经常能解决“改没改一样”的假象。第二日志比报错弹窗可靠得多。IIS的Failed Request Tracing失败请求跟踪是一个非常强大的工具开启以后能完整记录请求在IIS管线内每一步的处理结果。遇到解析不了的错误打开这个功能看请求追踪日志比自己瞎猜强得多。第三大文件上限不是越大越好。把IIS和ASP.NET的限制调到2GB不是不行但意味着如果有恶意请求或异常请求服务器最多要为一个请求准备2GB的资源和带宽。设计业务时还是应从上传场景出发比如普通内容站限制在200MB以内视频站再往上去到1GB、2GB。同时结合分片上传小分片对服务端的压力远小于超大单请求。第四别光测成功路径。上传成功后文件是否完整、能否被后续程序正常读取同样是验收标准。养成每次上传测试完后打开文件目录看一下文件体积是否等于源文件体积必要时做一次MD5比对。我遇到过配置全改好了上传也显示成功结果文件被截断一半的怪事后面才发现是后端写入逻辑里缓冲区设置得太小导致数据丢失。最后再分享一个小技巧如果可能的话给上传目录单独设置一个驱动器或磁盘分区不要把文件和系统、数据库放在一起。上传文件虽然看起来只是磁盘写入但高密度持续写入会显著影响同一块磁盘上其他服务的I/O性能。这个经验是我在线上环境吃过亏之后总结出来的。