Hướng dẫn tích hợp API lưu trữ S3/MinIO

Đội ngũ VPSTTT Ngày đăng: 21/6/2026 Cập nhật cuối: 24/7/2026 Lượt xem: 138 17 phút đọc 4.7/5Đánh giá92lượt đánh giá

Tài liệu hướng dẫn tích hợp API lưu trữ S3/MinIO theo chuẩn S3 Compatible: cấu hình endpoint, xác thực AWS Signature V4, presigned URL, AWS CLI, MinIO Client, SDK Node.js/PHP, CORS, policy bảo mật và checklist bàn giao.

Hướng dẫn S3 StorageMinIOAPIObject Storage

1. Tổng quan

MinIO là hệ thống lưu trữ object storage tương thích với Amazon S3. Ứng dụng của khách có thể upload,

download, liệt kê, xóa file và tạo link tải tạm thời thông qua S3 API.

Điểm quan trọng: request gọi trực tiếp đến API phải được ký bằng AWS Signature Version 4.

Không sử dụng Basic Auth hoặc Bearer Token thông thường cho các API S3.

Khái niệm chính

  • Bucket: vùng chứa file, tương tự thư mục gốc.
  • Object: file được lưu trong bucket.
  • Object key: đường dẫn của file, ví dụ invoices/2026/INV001.pdf.
  • Presigned URL: link có thời hạn để upload hoặc download mà không lộ secret key.

Luồng sử dụng phổ biến

  1. Hệ thống backend nhận file từ người dùng.
  2. Backend upload file lên MinIO/S3 bằng access key và secret key.
  3. Backend lưu lại bucket, object key, mime type, dung lượng vào database.
  4. Khi cần tải file, backend tạo presigned URL và trả về cho client.

2. Thông tin kết nối

Thay các giá trị mẫu dưới đây bằng thông tin thật trước khi bàn giao cho khách hàng.

Thông tinGiá trị mẫuGhi chú
API Endpointhttps://s3.example.comEndpoint dùng cho S3 API. Nên bật HTTPS.
Console Endpointhttps://console-s3.example.comTrang quản trị MinIO, chỉ cấp cho người có quyền vận hành.
Regionus-east-1MinIO thường dùng giá trị này nếu không cấu hình region riêng.
Access KeyCung cấp riêng qua kênh bảo mậtKhông gửi chung trong tài liệu công khai.
Secret KeyCung cấp riêng qua kênh bảo mậtKhông commit vào source code, không gửi qua chat công khai.
Bucket mặc địnhcustomer-filesCó thể tách bucket theo môi trường: dev, staging, production.
Path styletrueNên bật forcePathStyle khi dùng MinIO với SDK AWS.

Cấu trúc object key khuyến nghị

{module}/{yyyy}/{mm}/{uuid}-{safe-file-name}

Ví dụ:

orders/2026/06/8f3c9d7a-invoice-001.pdf

avatars/2026/06/user-1024.png

backups/2026/06/database-2026-06-20.sql.gz

Thông số bàn giao cần chốt trước khi tích hợp

NhómGiá trị cần cung cấpVí dụ điền thực tế
Môi trường productionEndpoint, bucket, region, giới hạn dung lượng filehttps://s3.company.vn, crm-prod, tối đa 50MB/file
Môi trường stagingEndpoint, bucket test, access key riêngcrm-staging, không dùng chung key production
Loại file cho phépDanh sách MIME typeimage/jpeg, image/png, application/pdf
Thời hạn linkThời gian sống của presigned URLUpload: 10 phút, Download: 15-60 phút
Retention/backupThời gian giữ file và chính sách xóaGiữ file hợp đồng tối thiểu 5 năm, file tạm xóa sau 7 ngày

Ma trận quyền đề xuất

Tài khoản/keyQuyềnDùng choKhông nên cấp
app-uploaders3:PutObject, s3:GetObject, s3:ListBucketỨng dụng upload và tạo link tải fileKhông cấp quyền quản trị user, không cấp toàn bộ bucket khác
app-readonlys3:GetObject, s3:ListBucketDịch vụ chỉ đọc file, đồng bộ dữ liệuKhông cấp s3:DeleteObject
backup-services3:PutObject, s3:GetObject, s3:DeleteObjectBackup định kỳ, dọn file cũ theo lịchKhông dùng chung với ứng dụng web

Ví dụ request S3 dạng REST

Ví dụ dưới đây minh họa đường dẫn và header. Giá trị Authorization phải do SDK hoặc thư viện ký

request tạo ra, không copy nguyên mẫu này để chạy thật.

PUT /customer-files/orders/2026/06/invoice.pdf HTTP/1.1 Host: s3.example.com Content-Type: application/pdf x-amz-date: 20260620T030000Z x-amz-content-sha256: <sha256-payload> Authorization: AWS4-HMAC-SHA256 Credential=<access-key>/20260620/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=<signature> Body: Nội dung file PDF dạng binary được gửi ở đây

Phần Body trong ví dụ là dữ liệu file thật. Khi dùng SDK hoặc fetch upload file, phần này chính là biến file/blob được gửi lên MinIO.

Ví dụ response khi upload thành công

HTTP/1.1 200 OK ETag: "9b2cf535f27731c974343645a3985328" x-amz-request-id: 183A6F4A9D1C4B2E Date: Sat, 20 Jun 2026 03:00:00 GMT

3. API backend đề xuất

Đây là lớp API nên đặt trong hệ thống backend của khách. Backend chịu trách nhiệm xác thực người dùng,

kiểm tra quyền nghiệp vụ, ghi database và tạo presigned URL. Frontend không gọi MinIO bằng secret key.

1. Tạo link upload

Thông tinChi tiết
EndpointPOST /api/files/presign-upload
Mục đíchTạo URL tạm thời để frontend upload file trực tiếp lên MinIO.
Thời hạn đề xuất5-10 phút.

Request

{ "module": "orders", "fileName": "invoice-001.pdf", "contentType": "application/pdf", "size": 245760, "ownerType": "order", "ownerId": "ORD-1001" }

Response

{ "success": true, "data": { "bucket": "customer-files", "objectKey": "orders/2026/06/8f3c9d7a-invoice-001.pdf", "uploadUrl": "https://s3.example.com/customer-files/orders/2026/06/8f3c9d7a-invoice-001.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256...", "method": "PUT", "expiresIn": 600, "requiredHeaders": { "Content-Type": "application/pdf" } } }

Frontend upload file bằng URL đã ký

await fetch(uploadUrl, { method: "PUT", headers: { "Content-Type": "application/pdf" }, body: file });

2. Xác nhận upload và lưu metadata

Thông tinChi tiết
EndpointPOST /api/files/complete
Mục đíchBackend kiểm tra object tồn tại trên MinIO, sau đó lưu bản ghi file vào database.

Request

{ "bucket": "customer-files", "objectKey": "orders/2026/06/8f3c9d7a-invoice-001.pdf", "originalName": "invoice-001.pdf", "contentType": "application/pdf", "ownerType": "order", "ownerId": "ORD-1001" }

Response

{ "success": true, "data": { "id": "file_01J0YQACV8K7Z1P6D4X9", "bucket": "customer-files", "objectKey": "orders/2026/06/8f3c9d7a-invoice-001.pdf", "size": 245760, "etag": "9b2cf535f27731c974343645a3985328", "createdAt": "2026-06-20T10:00:00+07:00" } }

3. Tạo link download

Thông tinChi tiết
EndpointGET /api/files/{id}/download
Mục đíchTrả về link tải file có thời hạn sau khi kiểm tra quyền người dùng.

Response

{ "success": true, "data": { "downloadUrl": "https://s3.example.com/customer-files/orders/2026/06/8f3c9d7a-invoice-001.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256...", "expiresIn": 900, "fileName": "invoice-001.pdf", "contentType": "application/pdf" } }

4. Xóa file

Thông tinChi tiết
EndpointDELETE /api/files/{id}
Mục đíchXóa object trên MinIO hoặc đánh dấu đã xóa trong database tùy chính sách dữ liệu.

Khuyến nghị vận hành: nếu file liên quan hóa đơn, hợp đồng hoặc dữ liệu pháp lý, nên dùng

soft delete trong database trước. Chỉ xóa object thật sau khi hết thời gian lưu trữ bắt buộc.

Bảng metadata nên lưu trong database

files - id UUID/string primary key - bucket varchar(100) - object_key varchar(1024) - original_name varchar(255) - content_type varchar(120) - size bigint - etag varchar(100) - owner_type varchar(50) - owner_id varchar(100) - uploaded_by user id - status uploaded | deleted | failed - created_at datetime - deleted_at datetime nullable

4. Xác thực

Hệ thống sử dụng cặp accessKeyId và secretAccessKey. Mỗi request S3 phải được ký bằng

AWS Signature V4. Thực tế nên dùng AWS SDK hoặc MinIO SDK để SDK tự ký request.

Không nên gọi S3 API trực tiếp từ trình duyệt bằng secret key.

Nếu frontend cần upload/download, backend nên tạo presigned URL có thời hạn ngắn rồi trả về cho frontend.

Biến môi trường khuyến nghị

S3_ENDPOINT=https://s3.example.com S3_REGION=us-east-1 S3_BUCKET=customer-files S3_ACCESS_KEY=your-access-key S3_SECRET_KEY=your-secret-key S3_FORCE_PATH_STYLE=true

Header xác thực khi gọi trực tiếp S3 API

Nếu khách không dùng SDK mà tự gọi HTTP, request cần có các header theo chuẩn AWS Signature V4.

Cách này dễ sai hơn SDK, chỉ nên dùng khi hệ thống của khách đã có thư viện ký request ổn định.

Header/Tham sốVí dụÝ nghĩa
Hosts3.example.comDomain endpoint S3/MinIO.
x-amz-date20260620T030000ZThời gian UTC dùng để ký request.
x-amz-content-sha256UNSIGNED-PAYLOAD hoặc SHA256 payloadHash nội dung request. Một số SDK dùng UNSIGNED-PAYLOAD cho presigned URL.
AuthorizationAWS4-HMAC-SHA256 Credential=...Chữ ký được tính từ method, path, query, header và secret key.

Presigned URL hoạt động như thế nào

  1. Frontend yêu cầu backend tạo link upload/download cho một file cụ thể.
  2. Backend kiểm tra quyền người dùng trong hệ thống của khách.
  3. Backend dùng secret key tạo presigned URL có thời hạn ngắn.
  4. Frontend dùng URL đó để upload/download trực tiếp với MinIO.
  5. Khi URL hết hạn, client phải xin URL mới, không dùng lại URL cũ.

Khuyến nghị: secret key chỉ nằm ở backend. Frontend/mobile app không được lưu secret key,

kể cả khi ứng dụng đã đóng gói hoặc obfuscate.

5. API S3 thường dùng

Các endpoint bên dưới mô tả theo chuẩn S3. Khi dùng SDK, bạn chỉ cần gọi command tương ứng, SDK sẽ tự tạo

method, path, header và chữ ký hợp lệ.

Nghiệp vụHTTPĐường dẫnGhi chú
Upload filePUT/{bucket}/{objectKey}Gửi nội dung file trong body. Nên set Content-Type.
Tải fileGET/{bucket}/{objectKey}Bucket private cần request đã ký hoặc presigned URL.
Kiểm tra metadataHEAD/{bucket}/{objectKey}Dùng để kiểm tra file tồn tại, dung lượng, mime type.
Xóa fileDELETE/{bucket}/{objectKey}Nên kiểm tra quyền trước khi xóa.
Liệt kê fileGET/{bucket}?list-type=2&prefix={prefix}Dùng prefix để lọc theo thư mục logic.

Header thường dùng khi upload

HeaderVí dụMục đích
Content-Typeapplication/pdfGiúp trình duyệt hiển thị hoặc tải file đúng kiểu.
Content-Dispositionattachment; filename="invoice.pdf"Gợi ý tên file khi download.
x-amz-meta-*x-amz-meta-owner-id: 1024Lưu metadata tùy chỉnh nếu cần.

6. Ví dụ AWS CLI

Có thể dùng AWS CLI để kiểm tra nhanh kết nối. Tất cả lệnh cần thêm --endpoint-url khi làm việc

với MinIO.

Cấu hình profile

aws configure --profile minio # Nhập: # AWS Access Key ID: your-access-key # AWS Secret Access Key: your-secret-key # Default region name: us-east-1 # Default output format: json

Liệt kê bucket

aws --profile minio \ --endpoint-url https://s3.example.com \ s3 ls

Upload file

aws --profile minio \ --endpoint-url https://s3.example.com \ s3 cp ./invoice.pdf s3://customer-files/orders/2026/06/invoice.pdf \ --content-type application/pdf

Download file

aws --profile minio \ --endpoint-url https://s3.example.com \ s3 cp s3://customer-files/orders/2026/06/invoice.pdf ./invoice.pdf

Tạo presigned URL để tải file

aws --profile minio \ --endpoint-url https://s3.example.com \ s3 presign s3://customer-files/orders/2026/06/invoice.pdf \ --expires-in 3600

7. MinIO Client

mc là công cụ dòng lệnh chính thức của MinIO, phù hợp cho thao tác quản trị, kiểm tra bucket,

phân quyền user và xem dung lượng.

Kết nối MinIO bằng alias

mc alias set company-s3 https://s3.example.com your-access-key your-secret-key # Kiểm tra kết nối mc admin info company-s3 mc ls company-s3

Tạo bucket và kiểm tra file

mc mb company-s3/customer-files mc ls company-s3/customer-files mc cp ./invoice.pdf company-s3/customer-files/orders/2026/06/invoice.pdf mc stat company-s3/customer-files/orders/2026/06/invoice.pdf

Tạo user cho ứng dụng

mc admin user add company-s3 app-uploader APP_UPLOADER_SECRET_CHANGE_ME

Policy mẫu cho ứng dụng upload/read

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::customer-files" ] }, { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::customer-files/*" ] } ] }

Gắn policy cho user

# Lưu policy vào file app-uploader-policy.json mc admin policy create company-s3 app-uploader-policy ./app-uploader-policy.json mc admin policy attach company-s3 app-uploader-policy --user app-uploader

Bật versioning cho bucket quan trọng

mc version enable company-s3/customer-files mc version info company-s3/customer-files

Lifecycle xóa file tạm sau 7 ngày

{ "Rules": [ { "ID": "delete-temp-files-after-7-days", "Status": "Enabled", "Filter": { "Prefix": "tmp/" }, "Expiration": { "Days": 7 } } ] }
# Lưu nội dung lifecycle vào lifecycle.json mc ilm import company-s3/customer-files < lifecycle.json mc ilm ls company-s3/customer-files

Kiểm tra dung lượng bucket

mc du company-s3/customer-files mc find company-s3/customer-files --name "*.pdf" --older-than 30d

8. Ví dụ SDK

Node.js với AWS SDK v3

import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import fs from "node:fs"; const s3 = new S3Client({ endpoint: process.env.S3_ENDPOINT, region: process.env.S3_REGION || "us-east-1", forcePathStyle: true, credentials: { accessKeyId: process.env.S3_ACCESS_KEY, secretAccessKey: process.env.S3_SECRET_KEY, }, }); const bucket = process.env.S3_BUCKET; const key = "orders/2026/06/invoice.pdf"; await s3.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: fs.createReadStream("./invoice.pdf"), ContentType: "application/pdf", })); const downloadUrl = await getSignedUrl( s3, new GetObjectCommand({ Bucket: bucket, Key: key }), { expiresIn: 3600 } ); console.log(downloadUrl);

Node.js tạo presigned URL cho frontend upload

import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; const s3 = new S3Client({ endpoint: process.env.S3_ENDPOINT, region: process.env.S3_REGION || "us-east-1", forcePathStyle: true, credentials: { accessKeyId: process.env.S3_ACCESS_KEY, secretAccessKey: process.env.S3_SECRET_KEY, }, }); export async function createUploadUrl({ bucket, key, contentType }) { const command = new PutObjectCommand({ Bucket: bucket, Key: key, ContentType: contentType, }); return getSignedUrl(s3, command, { expiresIn: 600 }); } export async function createDownloadUrl({ bucket, key, fileName }) { const command = new GetObjectCommand({ Bucket: bucket, Key: key, ResponseContentDisposition: `attachment; filename="${fileName}"`, }); return getSignedUrl(s3, command, { expiresIn: 900 }); }

PHP với aws/aws-sdk-php

<?php require __DIR__ . "/vendor/autoload.php"; use Aws\S3\S3Client; use Aws\S3\Exception\S3Exception; $s3 = new S3Client([ "version" => "latest", "region" => getenv("S3_REGION") ?: "us-east-1", "endpoint" => getenv("S3_ENDPOINT"), "use_path_style_endpoint" => true, "credentials" => [ "key" => getenv("S3_ACCESS_KEY"), "secret" => getenv("S3_SECRET_KEY"), ], ]); $bucket = getenv("S3_BUCKET"); $key = "orders/2026/06/invoice.pdf"; try { $s3->putObject([ "Bucket" => $bucket, "Key" => $key, "SourceFile" => __DIR__ . "/invoice.pdf", "ContentType" => "application/pdf", ]); $cmd = $s3->getCommand("GetObject", [ "Bucket" => $bucket, "Key" => $key, ]); $request = $s3->createPresignedRequest($cmd, "+60 minutes"); echo (string) $request->getUri(); } catch (S3Exception $e) { error_log($e->getAwsErrorCode() . ": " . $e->getMessage()); }

9. CORS & proxy

Nếu frontend upload trực tiếp bằng presigned URL, bucket cần cấu hình CORS cho domain ứng dụng.

Nếu không cấu hình CORS, trình duyệt sẽ chặn request dù URL đã ký đúng.

CORS mẫu cho bucket private

{ "CORSRules": [ { "AllowedOrigins": [ "https://app.example.com", "https://admin.example.com" ], "AllowedMethods": [ "GET", "PUT", "POST", "HEAD" ], "AllowedHeaders": [ "Authorization", "Content-Type", "x-amz-date", "x-amz-content-sha256", "x-amz-security-token", "x-amz-meta-*" ], "ExposeHeaders": [ "ETag", "Content-Length", "Content-Type" ], "MaxAgeSeconds": 3600 } ] }

Áp dụng CORS bằng AWS CLI

aws --profile minio \ --endpoint-url https://s3.example.com \ s3api put-bucket-cors \ --bucket customer-files \ --cors-configuration file://cors.json

Áp dụng CORS bằng MinIO Client

mc cors set company-s3/customer-files cors.json mc cors info company-s3/customer-files

có dữ liệu riêng tư. Hãy khai báo đúng domain frontend của khách.

Nginx reverse proxy mẫu cho MinIO API

server { listen 443 ssl http2; server_name s3.example.com; client_max_body_size 200M; location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 300; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_pass http://127.0.0.1:9000; } }

Nginx reverse proxy mẫu cho MinIO Console

server { listen 443 ssl http2; server_name console-s3.example.com; location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_pass http://127.0.0.1:9001; } }

10. Quy ước & bảo mật

Quy ước bucket

  • Không để bucket production ở chế độ public nếu chứa dữ liệu khách hàng, hợp đồng, hóa đơn hoặc thông tin cá nhân.
  • Tách bucket theo môi trường hoặc theo nhóm dữ liệu nếu cần phân quyền rõ ràng.
  • Không dùng tên file gốc làm object key duy nhất vì có thể trùng. Nên thêm UUID hoặc mã định danh.

Quy ước quyền truy cập

  • Access key dùng cho ứng dụng chỉ nên có quyền trên bucket cần thiết.
  • Tài khoản upload không nhất thiết có quyền xóa nếu nghiệp vụ không cần xóa.
  • Secret key phải lưu trong biến môi trường hoặc secret manager, không ghi trực tiếp vào source code.
  • Presigned URL nên có thời hạn ngắn, ví dụ 5 phút đến 60 phút tùy nghiệp vụ.

để phát hiện upload/download bất thường.

11. Lỗi thường gặp

Mã lỗiNguyên nhân phổ biếnCách xử lý
AccessDeniedAccess key không có quyền với bucket hoặc object.Kiểm tra policy, bucket name, object key và quyền read/write/delete.
NoSuchBucketBucket chưa tồn tại hoặc sai tên bucket.Tạo bucket hoặc sửa lại biến S3_BUCKET.
NoSuchKeyObject key không tồn tại.Kiểm tra lại đường dẫn object, phân biệt chữ hoa/chữ thường.
SignatureDoesNotMatchSai secret key, sai endpoint, sai region, hoặc request bị thay đổi sau khi ký.Kiểm tra credentials, region, đồng bộ thời gian server và bật path style cho MinIO.
RequestTimeTooSkewedThời gian máy client/server lệch quá nhiều.Đồng bộ NTP trên server ứng dụng.
EntityTooLargeFile vượt giới hạn upload của proxy, ứng dụng hoặc cấu hình hạ tầng.Kiểm tra Nginx/Apache, backend upload limit và cân nhắc multipart upload.
CORS errorBrowser bị chặn vì bucket chưa cho phép domain frontend.Kiểm tra AllowedOrigins, AllowedMethods, AllowedHeaders trong CORS.
403 khi dùng presigned URLURL hết hạn, method không khớp, hoặc header upload khác với lúc ký.Nếu ký với Content-Type: application/pdf, frontend phải gửi đúng header đó khi upload.
InvalidAccessKeyIdAccess key sai, bị khóa hoặc không tồn tại trên MinIO.Kiểm tra user bằng mc admin user info và tạo key mới nếu cần.
Connection refusedMinIO service chưa chạy hoặc proxy trỏ sai port.Kiểm tra port API 9000, console 9001, firewall và upstream Nginx.

Checklist debug nhanh

  1. Kiểm tra endpoint có mở được HTTPS không: curl -I https://s3.example.com.
  2. Kiểm tra credentials bằng aws s3 ls --endpoint-url ... hoặc mc ls.
  3. Kiểm tra bucket name và object key có đúng chữ hoa/chữ thường không.
  4. Kiểm tra đồng hồ server ứng dụng có lệch giờ UTC không.
  5. Kiểm tra presigned URL còn hạn và frontend dùng đúng method PUT/GET.
  6. Kiểm tra CORS nếu lỗi chỉ xảy ra trên trình duyệt nhưng chạy bằng CLI vẫn thành công.

12. Checklist bàn giao

Hạng mụcTrạng tháiGhi chú
API endpoint HTTPS hoạt độngKiểm tra chứng chỉ SSL còn hạn.
Bucket production đã tạoGhi rõ tên bucket bàn giao.
Access key/secret key đã cấpGửi secret qua kênh bảo mật riêng.
Policy quyền đã giới hạnChỉ cấp quyền cần thiết cho ứng dụng.
Đã test upload/download/deleteLưu log hoặc ảnh chụp kết quả test nếu cần.
Cơ chế backup/replicationGhi rõ lịch backup và thời gian lưu trữ.
Người phụ trách hỗ trợTên, email, số điện thoại hoặc kênh ticket.

Mẫu thông tin bàn giao cho khách

API Endpoint: https://s3.example.com Console: https://console-s3.example.com Region: us-east-1 Bucket: customer-files Access Key: gửi riêng Secret Key: gửi riêng Path Style: true Presigned URL: khuyến nghị dùng cho frontend, thời hạn 5-60 phút

Test case nghiệm thu tích hợp

STTKịch bảnCách testKết quả đạt
1Backend tạo presigned upload URLGọi POST /api/files/presign-upload với file PDF hợp lệ.Nhận được uploadUrl, objectKey, expiresIn.
2Frontend upload fileGửi PUT file lên uploadUrl.MinIO trả HTTP 200 hoặc 204, có ETag.
3Backend xác nhận fileGọi POST /api/files/complete.Database có bản ghi file, size và content type đúng.
4Download file privateGọi GET /api/files/{id}/download rồi mở downloadUrl.File tải về đúng nội dung, URL hết hạn sau thời gian cấu hình.
5Chặn file sai định dạngUpload file .exe hoặc MIME type không cho phép.Backend từ chối trước khi tạo presigned URL.
6Chặn file quá dung lượngUpload file vượt giới hạn đã thống nhất.Backend trả lỗi rõ ràng, không tạo object trên MinIO.
7Kiểm tra quyền userUser A thử tải file thuộc User B.Backend trả 403 Forbidden, không trả presigned URL.
8Xóa hoặc soft deleteGọi DELETE /api/files/{id}.Trạng thái file đúng theo chính sách: xóa object hoặc đánh dấu deleted.

Mẫu phản hồi lỗi API nội bộ

{ "success": false, "error": { "code": "FILE_TYPE_NOT_ALLOWED", "message": "Định dạng file không được hỗ trợ.", "details": { "allowedTypes": ["image/jpeg", "image/png", "application/pdf"] } } }

VPSTTT – Nhà Cung Cấp Giải Pháp Hạ Tầng Số Toàn Diện

VPSTTT là nhà cung cấp VPS, Hosting, máy chủ, Proxy và giải pháp lưu trữ dữ liệu tại Việt Nam, phục vụ cá nhân, lập trình viên và doanh nghiệp. Hệ sinh thái dịch vụ của VPSTTT gồm VPS Việt Nam, VPS quốc tế, VPS Anti DDoS, Hosting cPanel, máy chủ riêng, thuê Rack, Proxy IPv4/IPv6, S3 MinIO và Cloud Storage.

Bên cạnh việc cung cấp tài nguyên hạ tầng, VPSTTT còn tập trung hỗ trợ khách hàng trong quá trình triển khai, cấu hình và xử lý các sự cố thực tế. Nội dung trên Blog VPSTTT được xây dựng từ những tình huống thường gặp khi vận hành VPS, máy chủ và hệ thống trực tuyến, giúp người đọc có thể hiểu nguyên nhân và áp dụng giải pháp rõ ràng hơn.

VPSTTT – Hạ tầng ổn định, giải pháp linh hoạt và đồng hành trong suốt quá trình vận hành.

Thông tin liên hệ đội ngũ VPSTTT

Hotline: 0328 812 674

Website: https://vpsttt.com

Facebook: https://facebook.com/VPSTTT

Zalo OA: https://zalo.me/vpstttgroup