306 lines
75 KiB
JavaScript
306 lines
75 KiB
JavaScript
import{_ as i,o as a,c as n,a2 as p}from"./chunks/framework.B-QBKwc1.js";const g=JSON.parse('{"title":"文件上传全流程","description":"文件处理分为两个阶段:上传到临时目录(upload_file)→ 入库时迁移到正式目录(save_file)。","frontmatter":{},"headers":[],"relativePath":"zh/tools/upload.md","filePath":"zh/tools/upload.md"}'),l={name:"zh/tools/upload.md"};function h(t,s,k,e,d,r){return a(),n("div",null,[...s[0]||(s[0]=[p(`<h1 id="_7-1-文件上传全流程" tabindex="-1">7.1 文件上传全流程 <a class="header-anchor" href="#_7-1-文件上传全流程" aria-label="Permalink to "7.1 文件上传全流程""></a></h1><div class="tip custom-block"><p class="custom-block-title">概述</p><p>文件处理分为两个阶段:上传到临时目录(upload_file)→ 入库时迁移到正式目录(save_file)。这种"两阶段"设计将文件上传与业务逻辑解耦,前端先上传获取临时 URL,提交表单时 Logic 层自动将临时文件迁移到正式目录。</p></div><h2 id="完整流程图" tabindex="-1">完整流程图 <a class="header-anchor" href="#完整流程图" aria-label="Permalink to "完整流程图""></a></h2><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>┌─────────────────────────────────────────────────────────────────────┐</span></span>
|
||
<span class="line"><span>│ 第一阶段:上传 │</span></span>
|
||
<span class="line"><span>└─────────────────────────────────────────────────────────────────────┘</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>前端 后端</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │── POST /api/upload ────────►│</span></span>
|
||
<span class="line"><span> │ Content-Type: │</span></span>
|
||
<span class="line"><span> │ multipart/form-data │</span></span>
|
||
<span class="line"><span> │ file: <二进制> │</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │ ┌──────┴──────┐</span></span>
|
||
<span class="line"><span> │ │upload_file()│</span></span>
|
||
<span class="line"><span> │ │ │</span></span>
|
||
<span class="line"><span> │ │ 1. 检查上传状态</span></span>
|
||
<span class="line"><span> │ │ 2. finfo 检测真实 MIME</span></span>
|
||
<span class="line"><span> │ │ 3. 按类型校验扩展名</span></span>
|
||
<span class="line"><span> │ │ 4. 校验文件大小</span></span>
|
||
<span class="line"><span> │ │ 5. 图片额外 getimagesize 校验</span></span>
|
||
<span class="line"><span> │ │ 6. 保存到 temp/日期/随机文件名</span></span>
|
||
<span class="line"><span> │ └──────┬──────┘</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │◄── { fileUrl, filePath, ────│</span></span>
|
||
<span class="line"><span> │ originalName, fileSize, │</span></span>
|
||
<span class="line"><span> │ fileExtension, fileType }│</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │ 前端显示预览 │</span></span>
|
||
<span class="line"><span> │ 或暂存 URL │</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>┌─────────────────────────────────────────────────────────────────────┐</span></span>
|
||
<span class="line"><span>│ 第二阶段:入库 │</span></span>
|
||
<span class="line"><span>└─────────────────────────────────────────────────────────────────────┘</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>前端 后端</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │── POST /api/example/add ──►│</span></span>
|
||
<span class="line"><span> │ { name: "测试", │</span></span>
|
||
<span class="line"><span> │ avatar: "https:// │</span></span>
|
||
<span class="line"><span> │ cdn.example.com/ │</span></span>
|
||
<span class="line"><span> │ temp/20260920/ │</span></span>
|
||
<span class="line"><span> │ abc123.jpg" } │</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │ ┌──────┴──────┐</span></span>
|
||
<span class="line"><span> │ │ BaseLogic │</span></span>
|
||
<span class="line"><span> │ │ ::add() │</span></span>
|
||
<span class="line"><span> │ └──────┬──────┘</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │ ┌──────┴──────┐</span></span>
|
||
<span class="line"><span> │ │ processFile │</span></span>
|
||
<span class="line"><span> │ │ FieldsOn │</span></span>
|
||
<span class="line"><span> │ │ Save() │</span></span>
|
||
<span class="line"><span> │ └──────┬──────┘</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │ ┌──────┴──────┐</span></span>
|
||
<span class="line"><span> │ │ save_file() │</span></span>
|
||
<span class="line"><span> │ │ │</span></span>
|
||
<span class="line"><span> │ │ 1. 去掉域名得到相对路径</span></span>
|
||
<span class="line"><span> │ │ 2. 包含 temp/ → 移动到正式目录</span></span>
|
||
<span class="line"><span> │ │ 3. 外部URL → 下载保存</span></span>
|
||
<span class="line"><span> │ └──────┬──────┘</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │ ┌──────┴──────┐</span></span>
|
||
<span class="line"><span> │ │ 数据库存储 │</span></span>
|
||
<span class="line"><span> │ │ /uploads/ │</span></span>
|
||
<span class="line"><span> │ │ example/ │</span></span>
|
||
<span class="line"><span> │ │ 20260920/ │</span></span>
|
||
<span class="line"><span> │ │ abc123.jpg │</span></span>
|
||
<span class="line"><span> │ └──────┬──────┘</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │◄── { code:0, data:{id:1} }─│</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>┌─────────────────────────────────────────────────────────────────────┐</span></span>
|
||
<span class="line"><span>│ 第三阶段:读取 │</span></span>
|
||
<span class="line"><span>└─────────────────────────────────────────────────────────────────────┘</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>前端 后端</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │── GET /api/example/detail/1►│</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │ ┌──────┴──────┐</span></span>
|
||
<span class="line"><span> │ │processFile │</span></span>
|
||
<span class="line"><span> │ │FieldsOnRead │</span></span>
|
||
<span class="line"><span> │ │() │</span></span>
|
||
<span class="line"><span> │ └──────┬──────┘</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │ ┌──────┴──────┐</span></span>
|
||
<span class="line"><span> │ │get_file_url │</span></span>
|
||
<span class="line"><span> │ │() │</span></span>
|
||
<span class="line"><span> │ │拼接域名 │</span></span>
|
||
<span class="line"><span> │ └──────┬──────┘</span></span>
|
||
<span class="line"><span> │ │</span></span>
|
||
<span class="line"><span> │◄── { avatar: "https://──────│</span></span>
|
||
<span class="line"><span> │ cdn.example.com/uploads/ │</span></span>
|
||
<span class="line"><span> │ example/20260920/ │</span></span>
|
||
<span class="line"><span> │ abc123.jpg" } │</span></span></code></pre></div><h2 id="upload-file-函数" tabindex="-1">upload_file 函数 <a class="header-anchor" href="#upload-file-函数" aria-label="Permalink to "upload_file 函数""></a></h2><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// app/common.php</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">/**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 上传文件到临时目录</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 处理流程:</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 1. 检查上传状态,细分错误码</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 2. 用 finfo 检测真实 MIME(防止伪造扩展名)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 3. 按类型校验扩展名白名单</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 4. 校验文件大小(config/file.php → max_size)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 5. 图片额外用 getimagesize 校验(防止非图片伪装)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 6. 在 temp/日期/ 下保存,随机文件名</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@param</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> $formName 表单字段名</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@param</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> $type 文件类型:image|file</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@return</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> 成功返回 [url, path, name, size, ext, mime];失败返回 [error]</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">function</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> upload_file</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">formName</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">file</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">type</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">file</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">:</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // ... 完整实现见 app/common.php</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="参数说明" tabindex="-1">参数说明 <a class="header-anchor" href="#参数说明" aria-label="Permalink to "参数说明""></a></h3><table tabindex="0"><thead><tr><th>参数</th><th>类型</th><th>默认值</th><th>说明</th></tr></thead><tbody><tr><td><code>$formName</code></td><td>string</td><td><code>'file'</code></td><td>表单字段名</td></tr><tr><td><code>$type</code></td><td>string</td><td><code>'file'</code></td><td>文件类型:<code>image</code>(仅图片)/ <code>file</code>(所有文件)</td></tr></tbody></table><h3 id="返回值" tabindex="-1">返回值 <a class="header-anchor" href="#返回值" aria-label="Permalink to "返回值""></a></h3><p><strong>成功:</strong></p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">[</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">url</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">https://cdn.example.com/temp/20260920/abc123.jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 完整 URL</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">path</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">temp/20260920/abc123.jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 相对路径</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">name</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">photo.jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 原始文件名</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">size</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 102400</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 文件大小(字节)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">ext</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 扩展名</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">mime</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">image/jpeg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // MIME 类型</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">]</span></span></code></pre></div><p><strong>失败:</strong></p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">[</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">error</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">错误信息</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">]</span></span></code></pre></div><h3 id="错误类型" tabindex="-1">错误类型 <a class="header-anchor" href="#错误类型" aria-label="Permalink to "错误类型""></a></h3><table tabindex="0"><thead><tr><th>错误信息</th><th>说明</th></tr></thead><tbody><tr><td>未选择文件</td><td>表单字段为空</td></tr><tr><td>文件上传失败</td><td>PHP 上传错误(代码 1-7)</td></tr><tr><td>不允许的文件类型</td><td>扩展名不在白名单中</td></tr><tr><td>文件大小超出限制</td><td>超过 config/file.php 中的 max_size</td></tr><tr><td>图片文件不合法</td><td>图片类型校验失败(getimagesize 验证)</td></tr></tbody></table><h2 id="save-file-函数" tabindex="-1">save_file 函数 <a class="header-anchor" href="#save-file-函数" aria-label="Permalink to "save_file 函数""></a></h2><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">/**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 保存文件(临时→正式)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 处理逻辑:</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 1. 带域名 URL → 去掉域名得到相对路径</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 2. 包含 temp/ → 移动到正式目录</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 3. 外部 URL → 下载保存</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 4. 已在正式目录 → 直接返回</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@param</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> $fileUrl 文件 URL(带域名)或相对路径</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@param</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> $saveDir 正式保存的子目录</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@return</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#999999;">|</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">false</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> 入库路径(不带域名),失败返回 false</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">function</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> save_file</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileUrl</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">saveDir</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> ''</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 1. 带域名URL → 去掉域名得到相对路径</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 2. 包含 temp/ → 移动到正式目录</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 3. 外部URL → 下载保存</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 4. 已在正式目录 → 直接返回</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="处理逻辑" tabindex="-1">处理逻辑 <a class="header-anchor" href="#处理逻辑" aria-label="Permalink to "处理逻辑""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>输入 URL: "https://cdn.example.com/temp/20260920/abc123.jpg"</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 去掉域名</span></span>
|
||
<span class="line"><span>相对路径: "temp/20260920/abc123.jpg"</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 包含 temp/ → 需要迁移</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 移动到正式目录</span></span>
|
||
<span class="line"><span>目标路径: "/uploads/example/20260920/abc123.jpg"</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 返回入库路径</span></span>
|
||
<span class="line"><span>输出: "/uploads/example/20260920/abc123.jpg"</span></span></code></pre></div><h2 id="get-file-url-函数" tabindex="-1">get_file_url 函数 <a class="header-anchor" href="#get-file-url-函数" aria-label="Permalink to "get_file_url 函数""></a></h2><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">/**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 获取文件完整 URL</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 将数据库中的相对路径拼接为完整 URL:</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * - 已是完整 URL → 原样返回</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * - 相对路径 → 拼接配置的域名</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * - 空值 → 返回空字符串</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@param</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> $path 数据库中的相对路径</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@return</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> 完整 URL</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">function</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> get_file_url</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">path</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">:</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 1. 空值 → 返回 ''</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 2. 已是 http/https 开头 → 原样返回</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 3. 拼接 config/file.php 中的 domain_url</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="转换示例" tabindex="-1">转换示例 <a class="header-anchor" href="#转换示例" aria-label="Permalink to "转换示例""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>数据库值: "/uploads/example/20260920/abc123.jpg"</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ get_file_url()</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 拼接 domain_url</span></span>
|
||
<span class="line"><span>输出: "https://cdn.example.com/uploads/example/20260920/abc123.jpg"</span></span></code></pre></div><h2 id="在-Logic-中配置" tabindex="-1">在 Logic 中配置 <a class="header-anchor" href="#在-Logic-中配置" aria-label="Permalink to "在 Logic 中配置""></a></h2><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ExampleLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> /**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 需要自动处理的文件上传字段</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@var</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">avatar</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">cover</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> /**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 文件保存的子目录</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@var</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileSaveDir</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">example</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="自动行为" tabindex="-1">自动行为 <a class="header-anchor" href="#自动行为" aria-label="Permalink to "自动行为""></a></h3><table tabindex="0"><thead><tr><th>操作</th><th>自动行为</th></tr></thead><tbody><tr><td><code>add()</code></td><td><code>processFileFieldsOnSave()</code> → 调用 <code>save_file()</code> 迁移临时文件</td></tr><tr><td><code>update()</code></td><td><code>processFileFieldsOnSave()</code> → 调用 <code>save_file()</code> 迁移临时文件</td></tr><tr><td><code>detail()</code></td><td><code>processFileFieldsOnRead()</code> → 调用 <code>get_file_url()</code> 补全域名</td></tr><tr><td><code>pageList()</code></td><td><code>processFileFieldsOnReadList()</code> → 调用 <code>get_file_url()</code> 补全域名</td></tr><tr><td><code>allList()</code></td><td><code>processFileFieldsOnReadList()</code> → 调用 <code>get_file_url()</code> 补全域名</td></tr></tbody></table><h3 id="多文件字段" tabindex="-1">多文件字段 <a class="header-anchor" href="#多文件字段" aria-label="Permalink to "多文件字段""></a></h3><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> UserLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> /**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 需要自动处理的文件上传字段</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@var</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">avatar</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> /**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 文件保存的子目录</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@var</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileSaveDir</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">user/avatar</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ArticleLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> /**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 需要自动处理的文件上传字段</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@var</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">cover</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">attachment</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> /**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 文件保存的子目录</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@var</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileSaveDir</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">article</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h2 id="前端上传接口" tabindex="-1">前端上传接口 <a class="header-anchor" href="#前端上传接口" aria-label="Permalink to "前端上传接口""></a></h2><h3 id="上传文件" tabindex="-1">上传文件 <a class="header-anchor" href="#上传文件" aria-label="Permalink to "上传文件""></a></h3><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">POST</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> /api/upload/uploadFile</span></span>
|
||
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">Content-Type:</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> multipart/form-data</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">file:</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> <</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">二进制文</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">件</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">></span></span></code></pre></div><p><strong>响应:</strong></p><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> "</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">code</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">"</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 0</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> "</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">data</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">"</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> "</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">fileUrl</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">"</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> "</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">https://cdn.example.com/temp/20260920/abc123.jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> "</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">filePath</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">"</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> "</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">temp/20260920/abc123.jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> "</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">originalName</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">"</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> "</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">photo.jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> "</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">fileSize</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">"</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 102400</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> "</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">fileExtension</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">"</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> "</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> "</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">fileType</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">"</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> "</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">image/jpeg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> "</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">fileName</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">"</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> "</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">abc123.jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="上传图片-指定-type-image-" tabindex="-1">上传图片(指定 type=image) <a class="header-anchor" href="#上传图片-指定-type-image-" aria-label="Permalink to "上传图片(指定 type=image)""></a></h3><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">POST</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> /api/upload/uploadFile?type=image</span></span>
|
||
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">Content-Type:</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> multipart/form-data</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">file:</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> <</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">二进制图</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">片</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">></span></span></code></pre></div><h3 id="响应字段说明" tabindex="-1">响应字段说明 <a class="header-anchor" href="#响应字段说明" aria-label="Permalink to "响应字段说明""></a></h3><table tabindex="0"><thead><tr><th>字段</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><code>fileUrl</code></td><td>完整 URL(含域名)</td><td><code>https://cdn.example.com/temp/...</code></td></tr><tr><td><code>filePath</code></td><td>相对路径</td><td><code>temp/20260920/abc123.jpg</code></td></tr><tr><td><code>originalName</code></td><td>原始文件名</td><td><code>photo.jpg</code></td></tr><tr><td><code>fileSize</code></td><td>文件大小(字节)</td><td><code>102400</code></td></tr><tr><td><code>fileExtension</code></td><td>扩展名</td><td><code>jpg</code></td></tr><tr><td><code>fileType</code></td><td>MIME 类型</td><td><code>image/jpeg</code></td></tr><tr><td><code>fileName</code></td><td>服务器文件名</td><td><code>abc123.jpg</code></td></tr></tbody></table><h2 id="前端集成" tabindex="-1">前端集成 <a class="header-anchor" href="#前端集成" aria-label="Permalink to "前端集成""></a></h2><h3 id="ElementPlus-上传组件" tabindex="-1">ElementPlus 上传组件 <a class="header-anchor" href="#ElementPlus-上传组件" aria-label="Permalink to "ElementPlus 上传组件""></a></h3><div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"><</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">template</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">></span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> <</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">el-upload</span></span>
|
||
<span class="line"><span style="--shiki-dark:#62E884;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;"> :action</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">'/api/upload/uploadFile'</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span></span>
|
||
<span class="line"><span style="--shiki-dark:#62E884;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;"> :headers</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">{ Authorization: \`Bearer \${token}\` }</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span></span>
|
||
<span class="line"><span style="--shiki-dark:#62E884;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;"> :on-success</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">handleUploadSuccess</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span></span>
|
||
<span class="line"><span style="--shiki-dark:#62E884;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;"> :before-upload</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">beforeUpload</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> ></span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> <</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">el-button</span><span style="--shiki-dark:#62E884;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;"> type</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">primary</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">></span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">上传文件</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"></</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">el-button</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">></span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> </</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">el-upload</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">></span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"></</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">template</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">></span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"><</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">script</span><span style="--shiki-dark:#62E884;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;"> setup</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">></span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> token</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> localStorage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">getItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">access_token</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">const</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> beforeUpload</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> (</span><span style="--shiki-dark:#FFB86C;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;">file</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =></span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> isLt10M</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> file</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">size</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> /</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1024</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> /</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1024</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> <</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 10</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> if</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> (</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">!</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">isLt10M</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> ElMessage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">error</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">文件大小不能超过 10MB</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> return</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> isLt10M</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">};</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">const</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> handleUploadSuccess</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> (</span><span style="--shiki-dark:#FFB86C;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;">response</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =></span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // response.data.fileUrl 是完整 URL</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 提交表单时使用此 URL</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> form</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">value</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">avatar</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> response</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">data</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileUrl</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">};</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"></</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">script</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">></span></span></code></pre></div><h3 id="提交表单" tabindex="-1">提交表单 <a class="header-anchor" href="#提交表单" aria-label="Permalink to "提交表单""></a></h3><div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 前端提交时,avatar 字段的值是上传接口返回的 fileUrl</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> formData</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#998418;"> name</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">测试</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#998418;"> avatar</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">https://cdn.example.com/temp/20260920/abc123.jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">};</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 后端 Logic 层自动:</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 1. 从 avatar 字段检测到临时文件 URL</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 2. 调用 save_file() 迁移到正式目录</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 3. 去掉域名,只存相对路径入库</span></span></code></pre></div><h2 id="配置项" tabindex="-1">配置项 <a class="header-anchor" href="#配置项" aria-label="Permalink to "配置项""></a></h2><div class="language-ini vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">; .env</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">[</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#59873A;">FILE</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">]</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">; 文件上传域名</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">DOMAIN_URL</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> https://cdn.example.com</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">; 最大文件大小(字节)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">MAX_SIZE</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> 10485760</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">; 允许的图片扩展名</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">ALLOW_IMAGE_EXT</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> jpg,jpeg,png,gif,bmp,webp</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">; 允许的文件扩展名</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">ALLOW_FILE_EXT</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> jpg,jpeg,png,gif,bmp,webp,doc,docx,xls,xlsx,pdf,zip,rar</span></span></code></pre></div><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// config/file.php</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">return</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">domain_url</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> env</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">file.domain_url</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> ''</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">),</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">max_size</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> env</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">file.max_size</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 10485760</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">),</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 10MB</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">allow_image_ext</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> env</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">file.allow_image_ext</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">jpg,jpeg,png,gif,bmp,webp</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">),</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">allow_file_ext</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> env</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">file.allow_file_ext</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">jpg,jpeg,png,gif,bmp,webp,doc,docx,xls,xlsx,pdf,zip,rar</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">),</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span></code></pre></div><h2 id="安全检查链" tabindex="-1">安全检查链 <a class="header-anchor" href="#安全检查链" aria-label="Permalink to "安全检查链""></a></h2><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>上传文件安全检查:</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 1. PHP 上传状态检查(UPLOAD_ERR_OK)</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 2. finfo 检测真实 MIME 类型(防止伪造扩展名)</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 3. 扩展名白名单校验</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 4. 文件大小限制(config/file.php → max_size)</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 5. 图片额外 getimagesize 校验(防止非图片伪装)</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 6. 生成随机文件名(防止路径遍历攻击)</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 7. 存储到 Web 根目录之外(或 Nginx 禁止执行 PHP)</span></span></code></pre></div><div class="language-nginx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">nginx</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># Nginx 配置:上传目录禁止执行 PHP</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">location</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> ~*</span><span style="--shiki-dark:#E7EE98;--shiki-light:#AB5E3F;"> /uploads/.*\\.php$ </span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> deny </span><span style="--shiki-dark:#BF9EEE;--shiki-light:#1E754F;">all</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">}</span></span></code></pre></div>`,49)])])}const c=i(l,[["render",h]]);export{g as __pageData,c as default};
|