
Trong quá trình xây dựng một ứng dụng web nói chung. Việc lưu trữ, xử lý các tệp tin từ các request của người dùng là một tác vụ cơ bản cần phải có. Trong các ứng dụng Java web cũng không là ngoại lệ. Bài viết này mình xin giới thiệu cách xây dựng một ứng dụng Spring Web MCV có khả năng xử lý và lưu trữ các tệp tin được gửi lên thông qua giao thức HTTP.
Bài viết này sẽ tập trung vào cách Xư lý file được upload multilpart-form cùng một số phương pháp lưu trữ cơ bản.
Cấu hình
Xây dựng bằng Spring MVC
Spring cho phép ta xử lý file thông qua một đối tượng MultipartResolver mà Spring cung cấp, Và để nó có thể hoạt động, ta cần phải cấu hình chúng.
Đầu tiên chúng ta cần 1 dependency sau
<dependency> |
Tiếp sau đó ta cần định nghĩa CommonsMultipartResolver Bean vào trong file cấu hình Spring của chúng ta
Java class annotation config
@Configuration @EnableWebMvc @ComponentScan(“hivetech.sang.*”) public class MutilpartFileConfig { @Bean(name = “multipartResolver”) public CommonsMultipartResolver multipartResolver() { CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); multipartResolver.setMaxUploadSize(100000); return multipartResolver; } } |
XML config
<beans xmlns=”https://www.springframework.org/schema/beans” xmlns:xsi=”https://www.w3.org/2001/XMLSchema-instance” xmlns:context=”https://www.springframework.org/schema/context” xsi:schemaLocation=”https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd”> <context:component-scan base-package=”hivetech.sang.*”/> <bean id=”multipartResolver” class=”org.springframework.web.multipart.commons.CommonsMultipartResolver”> <property name=”maxUploadSize” value=”1000000″/> </bean> </beans> |
Đối tượng CommonsMultipartResolver có nhiều các property khác nhau như: uploadTempDir, defaultEncoding, maxUploadFilePerSize,… Tuỳ vào nhu cầu ta có thể thêm các trường này để config như mong muốn
Vậy là ta đã cấu hình xong. Bây giờ ứng dụng Spring WebMVC của chúng ta đã có thể hỗ trợ upload file như mong muốn.
Xây dựng bằng Spring boot
Với một ứng dụng Spring Boot. Mọi thứ ở trên đều được hỗ trợ. Thậm chí Spring Boot còn khiến nó trở nên dễ dàng hơn nữa khi mà ta không cần phải thực hiện bất cứ công đoạn cấu hình nào. Mọi thứ đã được Spring tự động thêm vào khi chúng ta thêm Web module của ứng dụng Spring boot
<dependency> |
Việc còn lại ta cần làm là tuỳ chỉnh các thông số tương tự như với cách mà Spring MVC làm. Tất cả sẽ được định nghĩa trong file application.properties
spring.servlet.multipart.max-file-size=128KBspring.servlet.multipart.max-request-size=128KB #We can also control whether file uploading is enabled, and the location for temp file upload: spring.servlet.multipart.enabled=true #define the upload location by variable so that we can use the temporary location for different operating systems.spring.servlet.multipart.location=${java.io.tmpdir} |
Các cách lưu trữ file
Lưu trữ file có nhiều phương pháp khác nhau, Tuỳ từng trường hợp ta có thể lựa chọn các cách lưu trữ phù hợp, Các cách lưu trữ phổ biến nhất hiện nay ta có thể kể tới:
- Lưu trữ trực tiếp vào bộ nhớ của server và lưu tên file vào cơ sở dữ liệu
- Lưu trữ thẳng vào database dưới dạng binary
- Lưu trữ trên các nền tảng đám mây, các server lưu trữ chuyên biệt như GoogleDrive, MinIO, AWS S3,… thông qua các API được cung cấp
Lưu trữ trực tiếp
Đây là phương pháp thường thấy trong các ứng dụng nhỏ, dung lượng các file không lớn do sự đơn giản của nó. Tuy nhiên phương pháp này có nhiều nhược điểm khi mà dữ liệu được lưu trực tiếp sẽ gây tốn cho tài nguyên máy chủ, cùng với đó sẽ khó nâng cấp được khi hệ thống mở rộng
Ta tạo một đối tượng để có thể lấy ra thông tin về các custom setting được chúng ta tự định nghĩa trong file application.properties
Spring.servlet.multipart.max-file-size=128KB |
Tạo một object để đọc config ở trên
@ConfigurationProperties(prefix = “storage”) public class FileStoreConfig { /** * Folder location for storing files */ private String location; public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } } |
Tiếp đó ta sẽ tạo một đối tượng Service gồm các function cơ bản như lưu file, hiển thị
@Configuration |
Tiếp đó ta sẽ tạo một Controller để xử lý request
@RestController public class FileManagerLocal { @Autowired private FileManagerLocalService fileManagerLocalService; @GetMapping(“/hello”) public String hello() { return “hello”; } @GetMapping(“/list-file”) public List<Object> listUploadedFiles() throws IOException { return Arrays.asList(fileManagerLocalService.loadAll().toArray()); } @GetMapping(“/files/{filename:.+}”) public ResponseEntity<Resource> serveFile(@PathVariable String filename) { Resource file = fileManagerLocalService.loadAsResource(filename); return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, “attachment; filename=\”” + file.getFilename() + “\””).body(file); } @PostMapping(“/add-file”) public ResponseEntity handleFileUpload(@RequestParam(“file”) MultipartFile[] file, @RequestParam(“description”) String desc) { return ResponseEntity.ok().body(fileManagerLocalService.store(file)); } @ExceptionHandler(FileStoreException.class) public ResponseEntity<?> handleStorageFileNotFound(FileStoreException exc) { return new ResponseEntity(exc.getMessage(), HttpStatus.I_AM_A_TEAPOT); } } |
Ta sẽ test ứng dụng bằng Postman như sau

Lưu trữ trực tiếp vào database dưới dạng binary
Đây là phương pháp ít được sử dụng hơn do có nhiều nhược điểm như giới hạn về dung lượng, gây khó khăn trong truy xuất dữ liệu,… Tuy nhiên nếu như các tệp tin nhỏ có thể sử dụng phương pháp này nhưng không khuyến khích
Đầu tiên ta tạo một Entity với một trường annotation Lob với kiểu dữ liệu byte[]
@Table(name = "file_entity")public
class
FileEntity {
@Id
@GeneratedValue
@Column(name = "id")private
Long id;
private
String name;
private
String type;
@Lob
private
byte[] content;
}
Tiếp đó ta tạo một Repository để thao tác với CSDL
@Repository public interface FileDBRepo extends JpaRepository<FileEntity, Long> { FileEntity findByNameEquals(String filename); void deleteByNameEquals(String fileName); } |
Rồi sau đó ta cũng tạo một đối tượng Service cùng một số function cơ bản
@Service public class FileManagerDBServiceIlpm implements FileManagerDBService { @Autowired FileDBRepo fileDBRepo; @Override public FileEntity store(MultipartFile file) { String fileName = StringUtils.cleanPath(file.getOriginalFilename()); try { FileEntity FileDB = FileEntity.builder() .name(fileName) .type(file.getContentType()) .content(file.getBytes()) .build(); return fileDBRepo.save(FileDB); } catch (IOException e) { throw new FileStoreException(“Cant save file with cause “+e.getMessage(), e); } } @Override public List<FileEntity> store(MultipartFile[] files) { return Arrays.asList(files).stream().map(this::store).collect(Collectors.toList()); } @Override public void delete(String filename) { fileDBRepo.deleteByNameEquals(filename); } @Override public Resource loadAsResource(String filename) { return new ByteArrayResource(fileDBRepo.findByNameEquals(filename).getContent()); } } |
Tạo Controller để xử lý Request
@RestController() @RequestMapping(path = “/db-manager”) public class FileManagerDB { @Autowired FileManagerDBService fileManagerDBService; @GetMapping(“/hello”) public String hello() { return “hello”; } @GetMapping(“/files/{filename:.+}”) public ResponseEntity<Resource> serveFile(@PathVariable String filename) { Resource file = fileManagerDBService.loadAsResource(filename); return ResponseEntity.ok().body(file); } @PostMapping(“/add-file”) public ResponseEntity handleFileUpload(@RequestParam(“file”) MultipartFile[] file, @RequestParam(“description”) String desc) { return ResponseEntity.ok().body(fileManagerDBService.store(file)); } } |
Ta tiến hành upload một ảnh

Dữ liệu đã được lưu trong database dưới dạng nhị phân

Kiểu dữ liệu Blob trong mySQL có dung lượng lưu trữ lên tới 4GB (LONGBLOB)
Kết luận
Từ các ví dụ trên ta có thể thấy các cách lưu trữ khác nhau và tự rút ra được ưu nhược điểm. Cùng với đó là cách khởi tạo mà triển khai một ứng dụng Spring web hỗ trợ thao tác và xử lý file.
Ngoài ra chủ đề về upload và lưu trữ file trên các nền tảng đám mây sẽ được bổ sung trong 1 chủ đề khác.
Phần code chi tiết có thể xem tại đây Link
Phạm Đức Sang