- 实现基于 Laravel 11 和 Filament 3.X 的文档管理系统 - 添加用户认证和分组管理功能 - 实现文档上传、分类和权限控制 - 集成 Word 文档自动转换为 Markdown - 集成 Meilisearch 全文搜索引擎 - 实现文档在线预览功能 - 添加安全日志和审计功能 - 完整的简体中文界面 - 包含完整的项目文档和部署指南 技术栈: - Laravel 11.x - Filament 3.X - Meilisearch 1.5+ - Pandoc 文档转换 - Redis 队列系统 - Pest PHP 测试框架
6.5 KiB
6.5 KiB
文件名保留功能说明
功能概述
知识库系统现在会完整保留用户上传文档时的原始文件名,包括中文、特殊字符等。下载文档时,文件名将与上传时保持一致。
实现细节
1. Filament 表单配置
在 DocumentResource 的文件上传字段中添加了 preserveFilenames() 方法:
Forms\Components\FileUpload::make('file')
->label('文档文件')
->required()
->acceptedFileTypes([...])
->preserveFilenames() // 保留原始文件名
->disk('local')
->directory('documents/' . date('Y/m/d'))
// ...
2. 文件上传处理
CreateDocument 页面
protected function mutateFormDataBeforeCreate(array $data): array
{
if (isset($data['file'])) {
$filePath = $data['file'];
$originalFileName = basename($filePath); // 获取原始文件名
$data['file_path'] = $filePath;
$data['file_name'] = $originalFileName; // 保存原始文件名
// ...
}
return $data;
}
EditDocument 页面
protected function mutateFormDataBeforeSave(array $data): array
{
if (isset($data['file']) && $data['file'] !== $this->record->file_path) {
$filePath = $data['file'];
$originalFileName = basename($filePath); // 获取原始文件名
$data['file_path'] = $filePath;
$data['file_name'] = $originalFileName; // 保存原始文件名
// ...
}
return $data;
}
3. DocumentService 更新
上传方法
public function uploadDocument(...): Document
{
return DB::transaction(function () use (...) {
// 获取原始文件名
$originalFileName = $file->getClientOriginalName();
// 使用 storeAs 保存文件,保留原始文件名
$directory = 'documents/' . date('Y/m/d');
$filePath = $file->storeAs($directory, $originalFileName, 'local');
// 保存到数据库
$document = Document::create([
'file_path' => $filePath,
'file_name' => $originalFileName, // 原始文件名
// ...
]);
return $document;
});
}
下载方法
public function downloadDocument(Document $document, User $user): StreamedResponse
{
// 使用原始文件名作为下载文件名
return Storage::disk('local')->download(
$document->file_path,
$document->file_name // 原始文件名
);
}
支持的文件名格式
✅ 完全支持
-
中文文件名
- 示例:
知识库管理系统需求文档.docx - 上传后保留:✅
- 下载时保留:✅
- 示例:
-
英文文件名
- 示例:
Requirements Document.docx - 上传后保留:✅
- 下载时保留:✅
- 示例:
-
数字文件名
- 示例:
2024-Report.docx - 上传后保留:✅
- 下载时保留:✅
- 示例:
-
特殊字符文件名
- 示例:
文档(2024-01-01)_v1.0.docx - 支持的特殊字符:
()[]_- - 上传后保留:✅
- 下载时保留:✅
- 示例:
-
混合格式文件名
- 示例:
Project_项目文档_2024.docx - 上传后保留:✅
- 下载时保留:✅
- 示例:
文件存储结构
文件按日期组织存储:
storage/app/
└── documents/
└── 2024/
└── 12/
└── 04/
├── 知识库管理系统需求文档.docx
├── Requirements Document.docx
└── 文档(2024-01-01)_v1.0.docx
数据库字段
documents 表
file_path: 存储相对路径(如:documents/2024/12/04/知识库管理系统需求文档.docx)file_name: 存储原始文件名(如:知识库管理系统需求文档.docx)file_size: 文件大小(字节)mime_type: MIME 类型
浏览器兼容性
下载文件名编码
对于包含非 ASCII 字符(如中文)的文件名,系统会自动处理编码:
Content-Disposition: attachment; filename=document.docx; filename*=utf-8''%E7%9F%A5%E8%AF%86%E5%BA%93.docx
filename: ASCII 兼容的后备文件名filename*: UTF-8 编码的完整文件名(RFC 5987)
支持的浏览器
- ✅ Chrome 80+
- ✅ Firefox 75+
- ✅ Safari 13+
- ✅ Edge 80+
- ✅ 移动浏览器(iOS Safari, Chrome Mobile)
测试覆盖
测试文件:tests/Feature/DocumentFileNameTest.php
-
test_上传文档时保留原始文件名
- 验证上传后
file_name字段正确保存
- 验证上传后
-
test_下载文档时使用原始文件名
- 验证下载响应头包含原始文件名
-
test_中文文件名正确处理
- 验证中文文件名的完整支持
-
test_特殊字符文件名正确处理
- 验证特殊字符的正确处理
运行测试
php artisan test --filter=DocumentFileNameTest
注意事项
1. 文件名冲突
如果同一天上传了同名文件,后上传的文件会覆盖先上传的文件。建议:
- 用户在上传前检查文件名
- 或者在文件名中添加时间戳或版本号
2. 文件名长度限制
- 数据库字段
file_name限制:255 字符 - 文件系统限制:通常为 255 字节
- 建议文件名不超过 100 个字符
3. 特殊字符限制
某些特殊字符可能在不同操作系统上有限制:
- Windows: 不支持
< > : " / \ | ? * - Linux/Mac: 不支持
/
系统会自动处理这些字符,但建议避免使用。
安全考虑
1. 路径遍历攻击防护
- 使用
basename()提取文件名,防止路径遍历 - 文件存储在受控目录中
2. 文件名注入防护
- Laravel Storage 自动处理文件名转义
- 数据库使用参数化查询
3. XSS 防护
- 文件名在显示时会被 Blade 模板自动转义
- 下载响应头使用标准编码
未来改进
-
文件名冲突处理
- 自动添加序号:
文档.docx→文档(1).docx - 或添加时间戳:
文档.docx→文档_20240104_143022.docx
- 自动添加序号:
-
文件名验证
- 添加文件名格式验证
- 限制特殊字符使用
- 提供文件名建议
-
批量上传
- 支持批量上传多个文件
- 自动处理文件名冲突
-
文件名搜索
- 支持按文件名搜索文档
- 支持模糊匹配
总结
文件名保留功能确保了用户上传和下载文档时的一致性体验,特别是对中文文件名的完整支持,使得知识库系统更加符合中文用户的使用习惯。
所有功能都经过完整测试,确保在各种场景下都能正常工作。