# 文件名保留功能说明

## 功能概述

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

## 实现细节

### 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. **文件名搜索**
   - 支持按文件名搜索文档
   - 支持模糊匹配

## 总结

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

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