Files
KnowledgeBase/docs/FILENAME_PRESERVATION.md
Knowledge Base System acf549c43c feat: 初始化知识库系统项目
- 实现基于 Laravel 11 和 Filament 3.X 的文档管理系统
- 添加用户认证和分组管理功能
- 实现文档上传、分类和权限控制
- 集成 Word 文档自动转换为 Markdown
- 集成 Meilisearch 全文搜索引擎
- 实现文档在线预览功能
- 添加安全日志和审计功能
- 完整的简体中文界面
- 包含完整的项目文档和部署指南

技术栈:
- Laravel 11.x
- Filament 3.X
- Meilisearch 1.5+
- Pandoc 文档转换
- Redis 队列系统
- Pest PHP 测试框架
2025-12-05 14:44:44 +08:00

6.5 KiB
Raw Blame History

文件名保留功能说明

功能概述

知识库系统现在会完整保留用户上传文档时的原始文件名,包括中文、特殊字符等。下载文档时,文件名将与上传时保持一致。

实现细节

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 // 原始文件名
    );
}

支持的文件名格式

完全支持

  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_特殊字符文件名正确处理

    • 验证特殊字符的正确处理

运行测试

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. 文件名搜索

    • 支持按文件名搜索文档
    • 支持模糊匹配

总结

文件名保留功能确保了用户上传和下载文档时的一致性体验,特别是对中文文件名的完整支持,使得知识库系统更加符合中文用户的使用习惯。

所有功能都经过完整测试,确保在各种场景下都能正常工作。