Giao diện
Câu hỏi thường gặp (FAQ) — File Manager
Sử dụng
Làm sao đổi nơi lưu trữ từ local sang S3?
- Vào Settings → File Storage
- Nhấn Add storage → chọn loại S3
- Điền thông tin kết nối (region, bucket, credentials)
- Đánh dấu Default nếu muốn storage mới là mặc định
TIP
File cũ vẫn nằm trên local. Chỉ file mới sẽ được lưu vào S3. Nếu cần di chuyển file cũ, cần thực hiện migration thủ công.
Giới hạn kích thước file?
Giới hạn phụ thuộc vào nhiều tầng:
| Tầng | Cấu hình | Mặc định |
|---|---|---|
| Plugin | Rules trong storage config | Theo storage |
| Nginx | client_max_body_size | 1m (cần tăng!) |
| Node.js | Memory limit | Phụ thuộc --max-old-space-size |
| Cloud storage | Giới hạn của provider | Thường 5GB/file |
Để upload file lớn, cần tăng giới hạn ở tất cả các tầng.
Có thể giới hạn loại file cho phép upload không?
Có. Cấu hình MIME type filter trong storage rules:
image/*— chỉ ảnh (JPEG, PNG, GIF, WebP, v.v.)application/pdf— chỉ PDFimage/*,application/pdf,application/msword— ảnh, PDF, Word
Làm sao lấy URL truy cập file?
URL được tạo tự động khi truy vấn attachment. Plugin hook vào afterFind và tính URL dựa trên:
baseUrl+path+filename- Với ảnh: thêm
thumbnailRulecho URL preview
File có bị xóa khi xóa record không?
Có, trừ khi storage có cấu hình paranoid: true. Khi bật paranoid:
- Record trong database bị xóa
- File trên storage không bị xóa
- Hữu ích khi cần giữ file backup
Có thể dùng MinIO thay S3 không?
Có. MinIO tương thích S3 API. Cấu hình storage loại s3 với custom Endpoint trỏ đến MinIO server:
Endpoint: https://minio.your-domain.com
Region: us-east-1
Bucket: my-bucket
Access Key: minioadmin
Secret Key: minioadminCấu hình
Credentials nên lưu ở đâu?
Khuyến nghị: Lưu trong Environment Variables plugin:
S3_ACCESS_KEY=AKIA...
S3_SECRET_KEY=secret...
S3_BUCKET=my-uploadsTrong cấu hình storage, sử dụng template:
text
Access Key: $env.S3_ACCESS_KEY
Secret Key: $env.S3_SECRET_KEYSử dụng cú pháp {{$env.TÊN_BIẾN}} để tham chiếu biến môi trường.
Có thể cấu hình nhiều storage cùng lúc không?
Có. Tạo nhiều storage backend, mỗi cái với tên riêng. Trong collection, chỉ định storage cho trường attachment:
typescript
// Collection sử dụng storage cụ thể
{
template: 'file',
storage: 'my-s3-storage', // tên storage
}Thumbnail rule là gì?
Thumbnail rule là hậu tố URL cho ảnh preview, do cloud storage provider hỗ trợ:
| Provider | Ví dụ thumbnail rule |
|---|---|
| Alibaba OSS | ?x-oss-process=image/resize,w_200 |
| Tencent COS | ?imageMogr2/thumbnail/200x |
| AWS S3 | Cần CloudFront + Lambda@Edge |
Lỗi thường gặp
Upload thất bại với file lớn?
Kiểm tra theo thứ tự:
- Nginx: Tăng
client_max_body_size(ví dụ:100m) - Plugin: Kiểm tra rules giới hạn kích thước
- Cloud storage: Kiểm tra quota/limit của provider
- Timeout: Tăng timeout cho upload request
Lỗi "no linked or default storage provided"?
Không có storage nào được cấu hình hoặc storage mặc định đã bị xóa. Vào Settings → File Storage và tạo/đặt storage mặc định.
Lỗi khi xóa storage?
Không thể xóa:
- Storage mặc định (default)
- Storage đang được sử dụng bởi collection (có trường attachment tham chiếu)
Chuyển collection sang storage khác trước khi xóa.
File URL trả về 404?
Kiểm tra:
- Base URL trong storage config đúng không
- File thực sự tồn tại trên storage backend
- Quyền truy cập (bucket policy, ACL) cho phép public read
- Nếu dùng CDN, cache có thể chưa cập nhật
Lỗi "Failed to delete file"?
Plugin ném FileDeleteError khi không thể xóa file khỏi storage backend. Kiểm tra:
- Credentials còn hợp lệ không
- Bucket/container có quyền delete không
- File có tồn tại trên storage không (có thể đã bị xóa thủ công)
Nâng cao
Có thể đăng ký storage type tùy chỉnh không?
Có. Sử dụng API registerStorageType:
typescript
import { PluginFileManagerServer } from '@digiforce-nc/plugin-file-manager';
class MyCustomStorage extends StorageType {
make() { /* return multer engine */ }
delete(records) { /* delete files */ }
}
const fileManager = app.pm.get<PluginFileManagerServer>('file-manager');
fileManager.registerStorageType('my-storage', MyCustomStorage);Upload file từ server-side code?
typescript
const fileManager = app.pm.get<PluginFileManagerServer>('file-manager');
// Upload file
const data = await fileManager.uploadFile({
filePath: '/tmp/report.pdf',
storageName: 'my-s3-storage',
});
// Tạo file record trong collection
const record = await fileManager.createFileRecord({
collectionName: 'attachments',
filePath: '/tmp/report.pdf',
storageName: 'my-s3-storage',
values: { title: 'Báo cáo tháng 3' },
});Lấy file stream từ storage?
typescript
const fileManager = app.pm.get<PluginFileManagerServer>('file-manager');
const { stream, contentType } = await fileManager.getFileStream(attachmentRecord);Hữu ích khi cần xử lý file (chuyển đổi, nén, v.v.) mà không cần download qua URL.