📤 文件上传
ThinkAdmin 框架对文件上传功能进行了精心封装,提供安全、高效的文件上传解决方案。
📚 基础概念
🤔 基本介绍
文件上传是指用户将本地文件(图片、文档、视频等)通过浏览器上传到服务器的过程。ThinkAdmin 提供了完整的文件上传解决方案。
📋 上传流程
- 用户选择文件:在浏览器中选择要上传的文件
- 前端验证:检查文件类型、大小等
- 提交到服务器:通过 HTTP POST 请求提交文件
- 服务器处理:验证文件、保存文件、返回结果
- 返回给用户:显示上传结果(成功或失败)
🔒 安全考虑
- 文件类型验证:只允许上传指定类型的文件
- 文件大小限制:限制文件大小,防止服务器资源耗尽
- 文件名处理:使用 hash 命名,防止文件名冲突和路径遍历攻击
- 权限验证:只有登录用户才能上传文件
📖 相关概念
- MIME 类型:文件的媒体类型,如
image/jpeg、application/pdf - 文件扩展名:文件名后缀,如
.jpg、.pdf - 文件大小:文件占用的字节数
- 文件 hash:文件的唯一标识,用于判断文件是否重复
🚀 主要功能
- 多存储支持: 支持本地存储和多种云存储方案
- 安全上传: 所有上传接口需要用户登录验证
- 前端集成: 基于
admin.js的$(element).uploadFile()方法 - 接口协同: 与
admin/api.upload/接口协同工作 - 配置灵活: 支持后台动态配置存储参数
- 性能优化: 支持 CDN 加速和文件秒传
📋 存储对比
| 存储方式 | 不占服务器空间 | 上传大文件 | 支持CDN加速 | 是否推荐 |
|---|---|---|---|---|
| 本地服务器存储 | ❌ | ❌ | ❌ | ❌ |
| 自建 Alist 存储 | ✔️ | 可配 | ❌ | ✔️ |
| 七牛云空间存储 | ✔️ | ✔️ | ✔️ | ✔️ |
| 又拍云USS存储 | ✔️ | ✔️ | ✔️ | ✔️ |
| 阿里云OSS存储 | ✔️ | ✔️ | ✔️ | ✔️ |
| 腾讯云COS存储 | ✔️ | ✔️ | ✔️ | ✔️ |
🌟 云存储
带宽优化
- 本地存储问题: 文件加载消耗服务器带宽
- 并发限制: 带宽达到上限影响其他用户访问
- 用户体验: 等待时间过长影响用户体验
- 服务器压力: 大量文件请求会增加服务器负载
云存储优势
- 带宽释放: 文件加载不占用网站带宽,释放服务器资源
- 性能提升: 云空间强大的存储和传输能力
- CDN 加速: 支持 CDN 加速,提升加载速度,全球访问更快
- 成本优化: 降低服务器带宽成本,按需付费更经济
- 高可用性: 云存储提供高可用性和数据冗余保护
- 扩展性强: 存储容量可随时扩展,无需担心空间不足
📤 上传接口
文件上传通过 admin/api.upload/ 接口处理,该接口需要用户登录验证。ThinkAdmin 已经内置了完整的文件上传接口,开发者可以直接使用,也可以自定义上传逻辑。
🔗 接口地址
标准上传接口:
- 接口路径:
/admin/api.upload/index - 请求方法: POST
- 需要登录: ✅ 所有上传接口都需要用户登录验证
- 返回格式: JSON
为什么需要登录验证?
- 防止未授权用户上传恶意文件
- 记录上传操作日志,便于追踪
- 控制上传权限,不同用户可能有不同的上传限制
📋 接口返回格式
上传成功后,接口会返回标准的 JSON 格式数据:
{
"code": 1,
"info": "上传成功",
"data": {
"url": "https://example.com/upload/image.jpg",
"key": "image/ab/cd1234567890abcdef1234567890ab.jpg",
"hash": "cd1234567890abcdef1234567890ab",
"file": "image.jpg"
}
}返回字段说明:
code: 状态码,1 表示成功,0 表示失败info: 提示信息data.url: 文件的完整访问地址(可以直接在浏览器中打开)data.key: 文件的存储路径(用于后续操作,如删除)data.hash: 文件的 hash 值(用于判断文件是否重复)data.file: 原始文件名
💻 后端处理示例
ThinkAdmin 已经内置了文件上传接口,但如果你需要自定义上传逻辑,可以参考以下示例:
<?php
declare(strict_types=1);
namespace app\admin\controller\api;
use think\admin\Controller;
use think\admin\Storage;
/**
* 文件上传接口(自定义示例)
* @class Upload
* @package app\admin\controller\api
*/
class Upload extends Controller
{
/**
* 文件上传处理
* @login true // 需要登录验证
*/
public function index()
{
// 1. 获取上传的文件
$file = $this->request->file('file');
if (empty($file)) {
$this->error('请选择要上传的文件');
}
// 2. 验证文件类型(可选,系统已内置验证)
$allowExts = str2arr(sysconf('storage.allow_exts', 'jpg,png,gif'));
$ext = strtolower($file->extension());
if (!in_array($ext, $allowExts)) {
$this->error('不支持的文件类型!');
}
// 3. 验证文件大小(可选,系统已内置验证)
$maxSize = sysconf('storage.max_size', 2097152); // 默认 2MB
if ($file->getSize() > $maxSize) {
$this->error('文件大小超过限制!');
}
// 4. 生成文件名称(使用 hash 命名,防止冲突)
$filename = Storage::name($file->getPathname(), $ext, 'upload');
// 生成规则:upload/ab/cd1234567890abcdef1234567890ab.jpg
// 其中 ab/cd 是 hash 的前两位,用于目录分散
// 5. 上传文件到存储(自动选择配置的存储方式)
$result = Storage::instance()->set(
$filename,
file_get_contents($file->getPathname())
);
// 6. 返回结果
if ($result) {
$this->success('上传成功', $result);
} else {
$this->error('上传失败,请稍后重试!');
}
}
}代码说明:
$this->request->file('file'): 获取上传的文件对象Storage::name(): 生成安全的文件名称(使用 hash 命名)Storage::instance()->set(): 保存文件到配置的存储位置(本地或云存储)$this->success(): 返回成功响应$this->error(): 返回错误响应
📋 前端使用
ThinkAdmin 在 admin.js 中封装了文件上传功能,提供了简单易用的前端接口。开发者无需关心底层实现,只需调用相应的方法即可实现文件上传。
🎯 快速使用
ThinkAdmin 提供了三种快速上传方法,适用于常见场景:
// 方式1:上传单个视频
$(InputElement).uploadOneVideo();
// 适用于:视频上传场景
// 方式2:上传单个图片
$(InputElement).uploadOneImage();
// 适用于:头像、封面图等单图上传
// 方式3:上传多张图片
$(InputElement).uploadMultipleImage();
// 适用于:相册、商品图片等多图上传使用示例:
<!-- 单图片上传示例 -->
<input type="text" name="avatar" value="">
<script>
// 初始化单图片上传
$('[name=avatar]').uploadOneImage();
// 监听上传完成事件
$('[name=avatar]').on('change', function() {
console.log('图片上传成功,地址:', this.value);
// 可以在这里更新页面预览
});
</script>🔧 HTML 属性方式
除了 JavaScript 方法,还可以通过 HTML 的 data-* 属性来配置上传功能:
<!-- 基础用法 -->
<input type="text" name="FileInput">
<a data-file
data-type="png,jpg,jpeg"
data-field="FileInput">
上传文件
</a>属性说明:
data-file: 标识这是一个上传按钮data-type: 指定允许的文件类型(多个用逗号分隔)data-field: 指定上传成功后,文件地址保存到哪个 input 元素
📡 上传事件
ThinkAdmin 提供了完整的上传事件系统,可以在上传的各个阶段执行自定义逻辑:
<input type="text" name="FileInput">
<a data-file data-type="png,jpg,jpeg" data-field="FileInput">上传文件</a>
<script>
$(function () {
// 1. 捕获文件上传结果(最简单的方式)
$('[name=FileInput]').on('change', function () {
// 文件上传成功后,input 的值会自动更新为文件地址
console.log('文件地址:', this.value);
// 可以在这里更新页面预览、提交表单等
});
// 2. 监听上传过程的各个事件(高级用法)
$('[data-file]')
// 文件选择后触发
.on('upload.choose', function (files) {
console.log('选择了文件:', files);
// 可以在这里进行文件预检查
})
// 文件 hash 计算完成后触发(用于秒传功能)
.on('upload.hash', function (event, file) {
console.log('文件 hash:', file.hash);
// 可以在这里检查文件是否已存在
})
// 上传进度事件(大文件上传时很有用)
.on('upload.progress', function (event, obj) {
var percent = obj.number; // 上传进度 0-100
console.log('上传进度:', percent + '%');
// 可以在这里更新进度条
$('#progress-bar').css('width', percent + '%');
})
// 单个文件上传成功(多文件上传时,每个文件成功都会触发)
.on('upload.done', function (event, obj) {
console.log('文件上传成功:', obj.file.name);
console.log('文件地址:', obj.data.url);
// 可以在这里处理每个文件的上传结果
})
// 所有文件上传完成(多文件上传时,所有文件完成后触发)
.on('upload.complete', function (event) {
console.log('所有文件上传完成');
// 可以在这里执行后续操作,如提交表单
});
});
</script>事件说明:
upload.choose: 用户选择文件后触发,可以用于文件预检查upload.hash: 文件 hash 计算完成后触发,用于秒传功能upload.progress: 上传进度事件,适用于大文件上传upload.done: 单个文件上传成功,多文件上传时每个文件都会触发upload.complete: 所有文件上传完成,适用于多文件上传场景
⚙️ 参数配置
ThinkAdmin 支持通过 HTML 的 data-* 属性来配置上传行为,这种方式简单直观,适合快速开发。
⚠️ 注意事项:
- 如果上传组件包含文件选择按钮(BUTTON),建议将属性直接设置在按钮上
- 使用
data-field属性绑定表单元素,上传成功后会自动更新 input 的值并触发 change 事件
📤 上传类型
通过 data-file 属性指定上传类型:
<!-- 单文件上传 -->
<a data-file="one" data-field="FileInput">上传单个文件</a>
<!-- 多文件上传 -->
<a data-file="mut" data-field="FileInput">上传多个文件</a>
<!-- 单图上传(带图片选择器) -->
<a data-file="image" data-field="FileInput">选择图片</a>
<!-- 多图上传(带图片选择器) -->
<a data-file="images" data-field="FileInput">选择多张图片</a>参数说明:
data-file="one": 单文件上传,用户只能选择一个文件data-file="mut": 多文件上传,用户可以选择多个文件data-file="image": 单图上传,带图片预览和选择器data-file="images": 多图上传,带图片预览和选择器
🔒 安全限制
通过以下属性配置上传的安全限制:
<!-- 安全模式:文件保存到安全目录,不能直接通过 URL 访问 -->
<a data-file data-safe="true" data-field="FileInput">安全上传</a>
<!-- 限制文件大小:单位 KB,1024 表示 1MB -->
<a data-file data-size="1024" data-field="FileInput">限制 1MB</a>
<!-- 限制文件类型:只允许上传指定后缀的文件 -->
<a data-file data-type="png,jpg,gif,jpeg" data-field="FileInput">只允许图片</a>
<!-- 绑定表单元素:上传成功后,文件地址保存到这里 -->
<a data-file data-field="FileInput" data-type="pdf">上传 PDF</a>参数说明:
data-safe="true": 安全模式,文件保存到安全目录(仅本地存储支持)data-size="1024": 限制文件大小,单位 KBdata-type="png,jpg": 限制文件类型,多个用逗号分隔data-field="FileInput": 指定上传成功后,文件地址保存到哪个 input
☁️ 存储方式
可以指定使用特定的存储方式,不填写则使用后台默认配置:
<!-- 使用本地服务器存储 -->
<a data-file data-uptype="local" data-field="FileInput">本地存储</a>
<!-- 使用七牛云存储 -->
<a data-file data-uptype="qiniu" data-field="FileInput">七牛云</a>
<!-- 使用腾讯云 COS -->
<a data-file data-uptype="txcos" data-field="FileInput">腾讯云</a>
<!-- 使用又拍云 USS -->
<a data-file data-uptype="upyun" data-field="FileInput">又拍云</a>
<!-- 使用阿里云 OSS -->
<a data-file data-uptype="aliyun" data-field="FileInput">阿里云</a>使用场景:
- 某些文件需要存储到特定位置时,可以指定存储方式
- 不填写则使用后台系统配置的默认存储方式
🖼️ 图片处理
ThinkAdmin 支持图片压缩和裁剪功能,可以在上传时自动处理:
<!-- 图片压缩:压缩质量 0.1-1.0,值越小越模糊 -->
<a data-file="image"
data-quality="0.8"
data-field="FileInput">
压缩上传
</a>
<!-- 限制图片尺寸:自动缩放 -->
<a data-file="image"
data-max-width="800"
data-max-height="600"
data-field="FileInput">
限制尺寸
</a>
<!-- 图片裁剪:自动裁剪到指定尺寸 -->
<a data-file="image"
data-cut-width="400"
data-cut-height="400"
data-field="FileInput">
裁剪上传
</a>参数说明:
data-quality="0.8": 图片压缩质量,0.1-1.0,值越小越模糊但文件越小data-max-width="800": 限制图片最大宽度(单位 px,不需要写单位)data-max-height="600": 限制图片最大高度(单位 px,不需要写单位)data-cut-width="400": 图片裁剪宽度(单位 px)data-cut-height="400": 图片裁剪高度(单位 px)
实际应用:
- 头像上传:使用
data-cut-width和data-cut-height裁剪成正方形 - 缩略图:使用
data-max-width和data-max-height限制尺寸 - 节省空间:使用
data-quality压缩图片
📁 存储路径
可以指定文件存储的前缀路径:
<!-- 指定存储前缀 -->
<a data-file data-path="avatar" data-field="FileInput">头像上传</a>
<!-- 文件会保存到:avatar/ab/cd1234567890abcdef1234567890ab.jpg -->
<a data-file data-path="document" data-field="FileInput">文档上传</a>
<!-- 文件会保存到:document/ab/cd1234567890abcdef1234567890ab.pdf -->使用场景:
- 按业务类型分类存储文件
- 便于文件管理和清理
📝 完整案例
🖼️ 案例1:头像上传
头像上传通常需要裁剪成正方形,并限制文件大小:
<!-- HTML 部分 -->
<div class="avatar-upload">
<input type="hidden" name="avatar" value="">
<img id="avatar-preview" src="/static/images/default-avatar.png" style="width: 100px; height: 100px;">
<button type="button"
data-file="image"
data-field="avatar"
data-type="jpg,png"
data-size="512"
data-cut-width="200"
data-cut-height="200">
选择头像
</button>
</div>
<script>
$(function() {
// 监听上传成功事件
$('[name=avatar]').on('change', function() {
var url = this.value;
// 更新预览图
$('#avatar-preview').attr('src', url);
});
});
</script>说明:
data-cut-width="200"和data-cut-height="200": 裁剪成 200x200 的正方形data-size="512": 限制文件大小不超过 512KBdata-type="jpg,png": 只允许上传 JPG 和 PNG 格式
📸 案例2:商品图片上传
商品图片通常需要多张,并且需要限制尺寸:
<!-- HTML 部分 -->
<div class="goods-images">
<input type="hidden" name="images" value="">
<div id="image-list"></div>
<button type="button"
data-file="images"
data-field="images"
data-type="jpg,png"
data-max-width="800"
data-max-height="800">
添加图片
</button>
</div>
<script>
$(function() {
// 监听上传成功事件
$('[name=images]').on('change', function() {
var images = this.value.split(',');
var html = '';
images.forEach(function(url) {
if (url) {
html += '<img src="' + url + '" style="width: 100px; margin: 5px;">';
}
});
$('#image-list').html(html);
});
});
</script>说明:
data-file="images": 多图上传data-max-width="800"和data-max-height="800": 自动缩放,最大 800px- 多图上传时,文件地址用逗号分隔存储在 input 中
📄 案例3:文档上传
文档上传通常需要限制文件类型和大小:
<!-- HTML 部分 -->
<input type="text" name="document" value="" readonly>
<a data-file="one"
data-field="document"
data-type="pdf,doc,docx"
data-size="5120">
上传文档(最大 5MB)
</a>
<script>
$(function() {
// 监听上传成功事件
$('[name=document]').on('change', function() {
console.log('文档上传成功:', this.value);
// 可以在这里显示文档名称或下载链接
});
});
</script>说明:
data-type="pdf,doc,docx": 只允许上传 PDF 和 Word 文档data-size="5120": 限制文件大小不超过 5MB(5120KB)
🎬 案例4:视频上传
视频上传通常文件较大,需要显示上传进度:
<!-- HTML 部分 -->
<input type="text" name="video" value="">
<button type="button"
data-file="one"
data-field="video"
data-type="mp4,avi,mov"
data-size="102400">
上传视频(最大 100MB)
</button>
<div id="progress" style="display: none;">
<div class="progress-bar" style="width: 0%; background: #5FB878;">0%</div>
</div>
<script>
$(function() {
// 监听上传进度
$('[data-file]').on('upload.progress', function(event, obj) {
var percent = obj.number;
$('#progress').show();
$('.progress-bar').css('width', percent + '%').text(percent + '%');
});
// 监听上传完成
$('[data-file]').on('upload.complete', function() {
$('#progress').hide();
alert('视频上传完成!');
});
});
</script>说明:
data-size="102400": 限制文件大小不超过 100MB- 使用
upload.progress事件显示上传进度 - 使用
upload.complete事件处理上传完成后的操作
🔒 案例5:安全文件上传
某些敏感文件需要保存到安全目录,不能直接通过 URL 访问:
<!-- HTML 部分 -->
<input type="hidden" name="secure_file" value="">
<a data-file="one"
data-field="secure_file"
data-safe="true"
data-type="txt,key">
上传安全文件
</a>
<script>
$(function() {
$('[name=secure_file]').on('change', function() {
// 安全文件不能直接通过 URL 访问
// 需要通过后端接口验证权限后才能下载
console.log('安全文件已上传,key:', this.value);
});
});
</script>说明:
data-safe="true": 启用安全模式,文件保存到安全目录- 安全文件不能直接通过 URL 访问,需要通过后端接口验证权限
- 适用于:密钥文件、配置文件等敏感文件
❓ 常见问题
Q1:上传失败,提示"不支持的文件类型"?
原因:文件类型不在允许列表中。
解决方法:
- 检查
data-type属性是否包含该文件类型 - 检查后台系统配置中的允许文件类型
- 确保文件扩展名正确(如
.jpg而不是.jpeg)
Q2:上传大文件时很慢?
原因:大文件上传需要时间,这是正常现象。
优化建议:
- 使用云存储(支持大文件上传)
- 显示上传进度(使用
upload.progress事件) - 考虑使用分片上传(需要自定义实现)
Q3:如何实现文件秒传?
原理:通过文件 hash 值判断文件是否已存在。
实现方式:
$('[data-file]').on('upload.hash', function(event, file) {
// 检查文件是否已存在
$.post('/admin/api/check-file', {hash: file.hash}, function(result) {
if (result.exists) {
// 文件已存在,直接使用已有文件
$('[name=file]').val(result.url).trigger('change');
}
});
});Q4:如何自定义上传接口?
方法:创建自定义上传控制器,参考"后端处理示例"部分。
注意事项:
- 需要实现登录验证
- 需要验证文件类型和大小
- 需要使用
Storage类保存文件 - 需要返回标准格式的 JSON 响应
Q5:云存储配置在哪里?
位置:后台管理系统 → 系统管理 → 文件存储配置
配置项:
- 存储类型(本地、七牛云、阿里云等)
- 访问密钥
- 存储区域
- CDN 域名等
