Ví dụ Download file với Spring Boot

Xem thêm các chuyên mục:

1- Mục tiêu của bài học

Trong bài học này tôi sẽ hướng dẫn bạn tạo một ứng dụng Spring Boot có các chức năng để download các tập tin từ Web Server về máy tính địa phương. Chẳng hạn các file ảnh, zip, pdf,...
Sau đây là một vài cách để tạo chức năng download tập tin:
  1. ResponseEntity<InputStreamResource>: Viết một phương thức trả về đối tượng ResponseEntity, đối tượng này bao lấy (wrap) một đối tượng InputStreamResource (Là dữ liệu của tập tin mà người dùng download về).
  2. ResponseEntity<ByteArrayResource>: Viết một phương thức trả về đối tượng ResponseEntity, đối tượng này bao lấy (wrap) một đối tượng ByteArrayResource (Là dữ liệu của tập tin mà người dùng download về).
  3. HttpServletRespone: Ghi trực tiếp dữ liệu của tập tin cần download vào HttpServletRespone.
Với các tập tin có kích thước lớn, người dùng khi download về cần phải đợi một khoảng thời gian. Có một vài thông tin sau bạn cần phải cung cấp cho trình duyệt:

Content-Disposition

Content-Disposition: Là một thông tin trên phần Header của Response, nó cho biết nội dung dự kiến sẽ được hiển thị trên trình duyệt.
  • inline: nội dung sẽ hiển thị tự động.
  • attachment: Tập tin đính kèm.
  • form-data: Dữ liệu form.
  • ....
Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="java-tutorial.pdf"

Content-Length

contentLength: Đây là kích thước của nội dung (Tính theo đơn vị byte). Thông tin này giúp cho trình duyệt thông báo với người dùng kích thước nội dung chuẩn bị download. Vì vậy trong khi download trình duyệt có thể thông báo cho người dùng số lượng byte đã download được, hiển thị phần trăm đã được download, ước lượng thời gian còn lại.

Content-Type

Thông tin này giúp trình duyệt biết được nội dung này có thể mở bằng các ứng dụng nào, và gợi ý người dùng mở bằng một chương trình có sẵn trên máy tính của họ khi nội dung đã download thành công. Sét đặt Content-Type=application/octet-stream nếu bạn muốn trình duyệt download nội dung đó lập tức, không cần hỏi người dùng.
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="picture.png"


Content-Type: image/png
Content-Disposition: attachment; filename="picture.png"


Content-Type: image/png
Content-Disposition: inline; filename="picture.png"
Xem thêm:

2- Tạo dự án Spring Boot

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0%20http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.o7planning</groupId>
    <artifactId>SpringBootDownload</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>SpringBootDownload</name>
    <description>Spring Boot +Download Example</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
SpringBootDownloadApplication.java
package org.o7planning.sbdownload;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootDownloadApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootDownloadApplication.class, args);
    }
    
}

3- ResponseEntity

Viết một phương thức trả về đối tượng ResponseEntity, đối tượng này bao lấy (wrap) một đối tượng InputStreamResource (Là dữ liệu của tập tin mà người dùng download về).
Example1Controller.java
package org.o7planning.sbdownload.controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.servlet.ServletContext;

import org.o7planning.sbdownload.utils.MediaTypeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class Example1Controller {

    private static final String DIRECTORY = "C:/PDF";
    private static final String DEFAULT_FILE_NAME = "java-tutorial.pdf";

    @Autowired
    private ServletContext servletContext;

    // http://localhost:8080/download1?fileName=abc.zip
    // Using ResponseEntity<InputStreamResource>
    @RequestMapping("/download1")
    public ResponseEntity<InputStreamResource> downloadFile1(
            @RequestParam(defaultValue = DEFAULT_FILE_NAME) String fileName) throws IOException {

        MediaType mediaType = MediaTypeUtils.getMediaTypeForFileName(this.servletContext, fileName);
        System.out.println("fileName: " + fileName);
        System.out.println("mediaType: " + mediaType);

        File file = new File(DIRECTORY + "/" + fileName);
        InputStreamResource resource = new InputStreamResource(new FileInputStream(file));

        return ResponseEntity.ok()
                // Content-Disposition
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
                // Content-Type
                .contentType(mediaType)
                // Contet-Length
                .contentLength(file.length()) //
                .body(resource);
    }

}
MediaTypeUtils.java
package org.o7planning.sbdownload.utils;

import javax.servlet.ServletContext;

import org.springframework.http.MediaType;

public class MediaTypeUtils {

    // abc.zip
    // abc.pdf,..
    public static MediaType getMediaTypeForFileName(ServletContext servletContext, String fileName) {
        // application/pdf
        // application/xml
        // image/gif, ...
        String mineType = servletContext.getMimeType(fileName);
        try {
            MediaType mediaType = MediaType.parseMediaType(mineType);
            return mediaType;
        } catch (Exception e) {
            return MediaType.APPLICATION_OCTET_STREAM;
        }
    }
    
}

4- ResponseEntity

Viết một phương thức trả về đối tượng ResponseEntity, đối tượng này bao lấy (wrap) một đối tượng ByteArrayResource (Là dữ liệu của tập tin mà người dùng download về).
Example2Controller.java
package org.o7planning.sbdownload.controller;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.servlet.ServletContext;

import org.o7planning.sbdownload.utils.MediaTypeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class Example2Controller {

    private static final String DIRECTORY = "C:/PDF";
    private static final String DEFAULT_FILE_NAME = "java-tutorial.pdf";

    @Autowired
    private ServletContext servletContext;

    // http://localhost:8080/download2?fileName=abc.zip
    // Using ResponseEntity<ByteArrayResource>
    @GetMapping("/download2")
    public ResponseEntity<ByteArrayResource> downloadFile2(
            @RequestParam(defaultValue = DEFAULT_FILE_NAME) String fileName) throws IOException {

        MediaType mediaType = MediaTypeUtils.getMediaTypeForFileName(this.servletContext, fileName);
        System.out.println("fileName: " + fileName);
        System.out.println("mediaType: " + mediaType);

        Path path = Paths.get(DIRECTORY + "/" + DEFAULT_FILE_NAME);
        byte[] data = Files.readAllBytes(path);
        ByteArrayResource resource = new ByteArrayResource(data);

        return ResponseEntity.ok()
                // Content-Disposition
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + path.getFileName().toString())
                // Content-Type
                .contentType(mediaType) //
                // Content-Lengh
                .contentLength(data.length) //
                .body(resource);
    }

}

5- HttpServletRespone

Ghi trực tiếp dữ liệu của tập tin cần download vào HttpServletRespone.
Example3Controller.java
package org.o7planning.sbdownload.controller;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;

import org.o7planning.sbdownload.utils.MediaTypeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class Example3Controller {

    private static final String DIRECTORY = "C:/PDF";
    private static final String DEFAULT_FILE_NAME = "java-tutorial.pdf";

    @Autowired
    private ServletContext servletContext;

    // http://localhost:8080/download3?fileName=abc.zip
    // Using HttpServletResponse
    @GetMapping("/download3")
    public void downloadFile3(HttpServletResponse resonse,
            @RequestParam(defaultValue = DEFAULT_FILE_NAME) String fileName) throws IOException {

        MediaType mediaType = MediaTypeUtils.getMediaTypeForFileName(this.servletContext, fileName);
        System.out.println("fileName: " + fileName);
        System.out.println("mediaType: " + mediaType);

        File file = new File(DIRECTORY + "/" + fileName);

        // Content-Type
        // application/pdf
        resonse.setContentType(mediaType.getType());

        // Content-Disposition
        resonse.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName());

        // Content-Length
        resonse.setContentLength((int) file.length());

        BufferedInputStream inStream = new BufferedInputStream(new FileInputStream(file));
        BufferedOutputStream outStream = new BufferedOutputStream(resonse.getOutputStream());

        byte[] buffer = new byte[1024];
        int bytesRead = 0;
        while ((bytesRead = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, bytesRead);
        }
        outStream.flush();
        inStream.close();
    }

}

Xem thêm các chuyên mục: