# 文件名保留功能说明 ## 功能概述 知识库系统现在会完整保留用户上传文档时的原始文件名,包括中文、特殊字符等。下载文档时,文件名将与上传时保持一致。 ## 实现细节 ### 1. Filament 表单配置 在 `DocumentResource` 的文件上传字段中添加了 `preserveFilenames()` 方法: ```php Forms\Components\FileUpload::make('file') ->label('文档文件') ->required() ->acceptedFileTypes([...]) ->preserveFilenames() // 保留原始文件名 ->disk('local') ->directory('documents/' . date('Y/m/d')) // ... ``` ### 2. 文件上传处理 #### CreateDocument 页面 ```php 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 页面 ```php 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 更新 #### 上传方法 ```php 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; }); } ``` #### 下载方法 ```php public function downloadDocument(Document $document, User $user): StreamedResponse { // 使用原始文件名作为下载文件名 return Storage::disk('local')->download( $document->file_path, $document->file_name // 原始文件名 ); } ``` ## 支持的文件名格式 ### ✅ 完全支持 1. **中文文件名** - 示例:`知识库管理系统需求文档.docx` - 上传后保留:✅ - 下载时保留:✅ 2. **英文文件名** - 示例:`Requirements Document.docx` - 上传后保留:✅ - 下载时保留:✅ 3. **数字文件名** - 示例:`2024-Report.docx` - 上传后保留:✅ - 下载时保留:✅ 4. **特殊字符文件名** - 示例:`文档(2024-01-01)_v1.0.docx` - 支持的特殊字符:`()[]_-` - 上传后保留:✅ - 下载时保留:✅ 5. **混合格式文件名** - 示例:`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` 1. **test_上传文档时保留原始文件名** - 验证上传后 `file_name` 字段正确保存 2. **test_下载文档时使用原始文件名** - 验证下载响应头包含原始文件名 3. **test_中文文件名正确处理** - 验证中文文件名的完整支持 4. **test_特殊字符文件名正确处理** - 验证特殊字符的正确处理 ### 运行测试 ```bash php artisan test --filter=DocumentFileNameTest ``` ## 注意事项 ### 1. 文件名冲突 如果同一天上传了同名文件,后上传的文件会覆盖先上传的文件。建议: - 用户在上传前检查文件名 - 或者在文件名中添加时间戳或版本号 ### 2. 文件名长度限制 - 数据库字段 `file_name` 限制:255 字符 - 文件系统限制:通常为 255 字节 - 建议文件名不超过 100 个字符 ### 3. 特殊字符限制 某些特殊字符可能在不同操作系统上有限制: - Windows: 不支持 `< > : " / \ | ? *` - Linux/Mac: 不支持 `/` 系统会自动处理这些字符,但建议避免使用。 ## 安全考虑 ### 1. 路径遍历攻击防护 - 使用 `basename()` 提取文件名,防止路径遍历 - 文件存储在受控目录中 ### 2. 文件名注入防护 - Laravel Storage 自动处理文件名转义 - 数据库使用参数化查询 ### 3. XSS 防护 - 文件名在显示时会被 Blade 模板自动转义 - 下载响应头使用标准编码 ## 未来改进 1. **文件名冲突处理** - 自动添加序号:`文档.docx` → `文档(1).docx` - 或添加时间戳:`文档.docx` → `文档_20240104_143022.docx` 2. **文件名验证** - 添加文件名格式验证 - 限制特殊字符使用 - 提供文件名建议 3. **批量上传** - 支持批量上传多个文件 - 自动处理文件名冲突 4. **文件名搜索** - 支持按文件名搜索文档 - 支持模糊匹配 ## 总结 文件名保留功能确保了用户上传和下载文档时的一致性体验,特别是对中文文件名的完整支持,使得知识库系统更加符合中文用户的使用习惯。 所有功能都经过完整测试,确保在各种场景下都能正常工作。