使用 Spring Boot 和 MSSQL 下载文件

Zay*_*Lim 8 java sql-server spring download spring-boot

我正在使用 Spring Boot、JSP 和 MSSQL 上传和下载文件。我可以运行上传和下载功能,但从数据库下载的文件已损坏。谁能帮我这个?

这是我的春季版本

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.3-SNAPSHOT</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
Run Code Online (Sandbox Code Playgroud)

我的控制器

@RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
public ResponseEntity < Object > upload(@RequestPart(required = false) MultipartFile file) throws IOException {
    try {
        if (file != null) {
            tmDAO.storeFile(file, tm);
        }
        return new ResponseEntity < Object > ("success", HttpStatus.OK);
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}

@RequestMapping("/downloadFile/{id}")
public String downloadFile(@PathVariable("id") String id,
    HttpServletResponse res) throws IOException {
    FileModel fm = tmDAO.getFile(id);
    try {
        File file = new File(fm.getName());
        FileOutputStream fos = new FileOutputStream(file);
        res.setContentLength(fm.getData().length);
        res.setHeader("Content-Disposition", "attachment; filename=" + fm.getName());
        fos.write(fm.getData(), 0, fm.getData().length);
        fos.flush();
        fos.close();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
    return "string";
}
Run Code Online (Sandbox Code Playgroud)

服务

    
@Override
public FileModel storeFile(MultipartFile file, TeachingMaterial tm) throws IOException {
    String fileName = StringUtils.cleanPath(file.getOriginalFilename());
    FileModel fileModel = new FileModel(UUID.randomUUID().toString(), tm.getContributor(), fileName,
    file.getContentType(), file.getBytes(), tm.getClassName(), tm.getSubjectName(), tm.getSlots());
    return fileRepo.save(fileModel);
}

@Override
public FileModel getFile(String id) throws IOException {
    Optional < FileModel > fm = fileRepo.findById(id);
    if (fm.isPresent()) {
        return fm.get();
    }
    return null;
}
Run Code Online (Sandbox Code Playgroud)

文件模型.java

@Entity
@Table(name = "FILES")
public class FileModel {
    @Id
    private String id;
    private String contributor;
    private String name;
    private String type;
    @Lob
    private byte[] data;
    private String className;
    private String subjectName;
    private String slots;

    public FileModel() {
        super();
    }

    public FileModel(String id, String contributor, String name, String type, byte[] data, String className,
            String subjectName, String slots) {
        super();
        this.id = id;
        this.contributor = contributor;
        this.name = name;
        this.type = type;
        this.data = data;
        this.className = className;
        this.subjectName = subjectName;
        this.slots = slots;
    }
**I skips the getters and setters for simplication**
}
Run Code Online (Sandbox Code Playgroud)

在MSSQL中记录

在此输入图像描述

下载txt文件后我得到了什么

在此输入图像描述

原始数据应该是

测试123

ABC

有人可以帮我解决这个问题吗?

jcc*_*ero 0

我认为问题可能与您执行文件下载的方式有关:

@RequestMapping("/downloadFile/{id}")
public String downloadFile(@PathVariable("id") String id,
    HttpServletResponse res) throws IOException {
    FileModel fm = tmDAO.getFile(id);
    try {
        File file = new File(fm.getName());
        FileOutputStream fos = new FileOutputStream(file);
        res.setContentLength(fm.getData().length);
        res.setHeader("Content-Disposition", "attachment; filename=" + fm.getName());
        fos.write(fm.getData(), 0, fm.getData().length);
        fos.flush();
        fos.close();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
    return "string";
}
Run Code Online (Sandbox Code Playgroud)

请注意,在您的代码中,您正在将获得的FileModel数据写入 a FileOutputStream,但您应该将该数据写入HttpServletResponse底层OutputStream。请考虑对您的代码进行以下修改:

@RequestMapping("/downloadFile/{id}")
public void downloadFile(
    @PathVariable("id") String id,
    HttpServletResponse res
) throws IOException {
    // Define an auxiliary InputStream: more on this later
    InputStream in = null;
    // Define a reference to the response OutputStream. We will initialize
    // it later
    OutputStream out = null;
    try {
        FileModel fm = tmDAO.getFile(id);
        byte[] data = fm.getData();
        // initialize the response content-type (perhaps with type?)
        // and additional headers
        res.setContentType("application/octet-stream");
        res.setContentLength(data.length);
        res.setHeader("Content-Disposition", 
             "attachment; filename=" + fm.getName());
        
        // write data to output

        // This variable ideally should be defined as a constant
        int BUFFER_SIZE = 4096;

        byte[] buffer = new byte[BUFFER_SIZE];

        in = new ByteArrayInputStream(data);
        out = res.getOutputStream();

        while(data.read(buffer, 0, BUFFER_SIZE) != -1) {
            out.write(buffer, 0, BUFFER_SIZE);
        }

        out.flush();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    } finally {
        // cleanup
        if (in != null) {
            try {
                in.close(); 
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }

        if (out != null) {
            try {
                out.close(); 
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }

    // there is no need to return anything
}
Run Code Online (Sandbox Code Playgroud)

如果您愿意,可以使用Apache Commons IO 项目中方便的IOUtils类大大简化此过程:

@RequestMapping("/downloadFile/{id}")
public void downloadFile(
    @PathVariable("id") String id,
    HttpServletResponse res
) throws IOException {
    // Define a reference to the response OutputStream. We will initialize
    // it later
    OutputStream out = null;
    try {
        FileModel fm = tmDAO.getFile(id);
        byte[] data = fm.getData();
        // initialize the response content-type (perhaps with type?)
        // and additional headers
        res.setContentType("application/octet-stream");
        res.setContentLength(data.length);
        res.setHeader("Content-Disposition", 
             "attachment; filename=" + fm.getName());
        
        // write data to output
        
        out = res.getOutputStream();

        // As suggested by @Olivier in this comments, we can
        // get rid of the ByteArrayInputStream and use data directly
        IOUtils.copy(data, out);

        out.flush();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    } finally {
        // cleanup
        IOUtils.closeQuietly(out);
    }

    // there is no need to return anything
}
Run Code Online (Sandbox Code Playgroud)

Spring 提供了额外的方法来处理写入byte[]线路,但您的解决方案是一种简单且非常好的解决方案。