Chuyển tới nội dung chính

Lưu trữ tệp (OSS)

govex-cloud-oss cung cấp lớp lưu trữ file trừu tượng (Storage) thống nhất cho MinIO/S3, kèm REST API upload — xem/tải file hỗ trợ Range và cơ chế phân quyền truy cập file theo policy của ứng dụng.

Khi nào sử dụng

  • Cần upload và quản lý file (ảnh, tài liệu PDF, logo...) mà không muốn phụ thuộc trực tiếp vào SDK của MinIO/S3.
  • Cần endpoint xem/tải file cho frontend, có hỗ trợ tải từng phần (Range) cho file lớn.
  • Cần kiểm soát quyền truy cập file private theo nghiệp vụ (chủ sở hữu, hồ sơ, phòng ban...).

Cài đặt

Thêm dependency vào pom.xml — không khai version vì đã được quản lý qua BOM:

<dependency>
<groupId>vn.govex.cloud</groupId>
<artifactId>govex-cloud-oss</artifactId>
</dependency>

Nếu ứng dụng chưa dùng parent/BOM của Govex Cloud, xem hướng dẫn tại Cài đặt.

Cấu hình

Cấu hình lưu trữ chỉ đọc từ application.yml với prefix govex.oss.*:

PropertyMô tảMặc định
govex.oss.enabledCho phép khởi tạo MinIO từ cấu hình govex.oss.true
govex.oss.urlEndpoint MinIO/S3.
govex.oss.access-key / govex.oss.secret-keyCredential truy cập.
govex.oss.bucket-nameBucket chính.
govex.oss.public-bucket-nameBucket cho file công khai (MinIO).
govex.oss.tmp-bucket-nameBucket cho file tạm (MinIO).Dùng lại bucket chính
govex.oss.custom-domainDomain tùy chỉnh để tạo URL file.Dùng govex.oss.url
govex.oss.regionRegion (S3/MinIO).
govex.oss.access.default-deny-when-no-policyTừ chối file private khi không có policy nào xử lý path.true

Khi enabled=true và có đủ url, access-key, secret-key, bucket-name, bean storage khởi tạo MinIO. Nếu thiếu bất kỳ giá trị nào, dependency trả về null object (NoneStorage) và mọi thao tác file sẽ ném STORAGE_NOT_CONFIGURED.

Sử dụng

Cấu hình MinIO:

govex:
oss:
enabled: true
url: http://minio:9000
bucket-name: myapp
access-key: minioadmin
secret-key: minioadmin

Upload file qua Storage — caller chỉ làm việc với path opaque do upload() trả về:

@Service
@RequiredArgsConstructor
public class LogoService {

private final Storage storage;

public String uploadLogo(MultipartFile file) throws IOException {
return storage.upload(UploadRequest.builder()
.path("idp/logo")
.fileName(file.getOriginalFilename())
.inputStream(file.getInputStream())
.contentType(file.getContentType())
.accessLevel(AccessLevel.PUBLIC)
.build());
}
}

Các thao tác khác trên cùng path: stat (metadata), readBytes, openStream (có bản nhận offset/length để đọc từng phần), move, copy, delete, exists, isPublic. Muốn giữ chỗ đường dẫn trước khi upload (ví dụ tạo bản ghi trước, upload sau) dùng reservePath/uploadTo.

REST API sẵn có:

MethodEndpointMô tả
POST/api/v1/storage/uploadUpload multipart với các tham số file, access (public/private, mặc định private), path.
GET/api/v1/storage/file/**Xem file; hỗ trợ header Range cho file private, cache 7 ngày cho file public.
GET/api/v1/storage/download/**Tải file dạng attachment.
GET/api/v1/storage/view/getEndpoint cũ, đã deprecated — không dùng cho tích hợp mới.

Phân quyền file private theo nghiệp vụ bằng cách khai báo FileAccessPolicy:

@Component
public class OwnerFileAccessPolicy implements FileAccessPolicy {

@Override
public boolean canAccess(String storagePath, Authentication authentication) {
return fileOwnerService.isOwner(storagePath, authentication.getName());
}

@Override
public boolean supports(String storagePath) {
return storagePath.startsWith("document/");
}
}

Lưu ý

  • Cần hạ tầng MinIO/S3 sẵn sàng và cấu hình hợp lệ; nếu không có cấu hình, dependency vẫn nạp bình thường nhưng mọi thao tác file ném STORAGE_NOT_CONFIGURED.
  • Upload qua REST chỉ chấp nhận JPEG, PNG, SVG, ICO và PDF; file không thuộc danh sách bị từ chối.
  • File private bị từ chối mặc định khi không có FileAccessPolicy nào xử lý path (access.default-deny-when-no-policy=true). Cân nhắc đặt false nếu muốn mọi người dùng đã xác thực đều truy cập được, hoặc bổ sung policy phù hợp.
  • Ứng dụng có thể tự khai báo bean quản lý truy cập riêng; bean mặc định chỉ được tạo khi context chưa có bean tương ứng.