我们在springboot做web项目时,经常需要进行文件下载。比如导出pdf,excel,txt等等文件格式。今天总结一下关于文件下载的一些理解。



输出流  

  在web开发中 HttpServletResponse 对象来对前端做出响应,而 spring mvc对返回数据做了格式化,就不需要我们手动去格式化这些数据啦。不过我们今天的重点是他 OutputStream os = response.getOutputStream();  来进行文件传输。我们接下来的任务就是输入输出流的学习。


简单的demo

@RequestMapping("student_pdf")
public Map StudentPdf(HttpServletRequest request,HttpServletResponse response, String stu_no) throws UnsupportedEncodingException {
    if (stu_no == null)
        return null;
    //拿到该学生的记录
    Student student = studentPOJO.getStudentByStuNo(stu_no);
    if (student == null)
        return null;
    String fileName = new String(student.getStu_name().getBytes("UTF-8"),"ISO8859-1");
    //设置头部
    response.setCharacterEncoding("utf-8");
    response.setHeader("content-type", "application/pdf");
    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition", String.format("attachment; filename=%s_%s.pdf", fileName,student.getStu_no()));
    //将student转成map
    HashMap<String,Object> studentMap = (HashMap) TransBeanMap.transBeanToMap(student);
    try {
        OutputStream outputStream = response.getOutputStream();
        WriteContent pdf = new WriteContent(PageSize.A4,outputStream,excelDictionary);
        pdf.open();
        pdf.setTitle("大学信息工程学院学生信息");
        pdf.close();
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
    return null;
}

当然,这里面出现了很多其他代码,很简单的需求就是,我需要根据数据库导出的数据,生成一个pdf文件,返给前端。 那么我们直击准点。

WriteContent pdf = new WriteContent(PageSize.A4,outputStream,excelDictionary);

可以看见,我们直接把outputStream输出流给了,我们生成pdf的类,其实这的意思就是,将paf生成的内容,让他输出到这个流上,这样我们就能直接拿到文件数据啦。前端进行接收。


所有得到最基本的结论,将文件输出流直接定向到 outputStream 就能实现文件的传输。


进一步

上面这只是单文件,那我们如果想多文件呢。那对于很浅显的办法就是把文件压缩,然后将压缩包也是单文件传输,这固然没问题,但是你会不会想到,先在服务器上创建这么一个压缩包。我可以很负责任告诉你,不用。ok,那我们开始。


当我们多个文件时,我们确实需要压缩,但是我们并不需要将所有的文件创建出来,在压缩,而是直接利用流来操作。很大程度上提高性能。

@RequestMapping("student_pdfs")
public Map StudentPdfs(HttpServletRequest request,HttpServletResponse response, @RequestBody List<String> stu_nos) throws IOException {
    if (stu_nos == null || stu_nos.size() == 0)
        return null;
    //设置头部
    response.setCharacterEncoding("utf-8");
    response.setHeader("content-type", "application/pdf");
    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition", String.format("attachment; filename=%s.zip","学生基本信息"));
    //生成压缩流包裹压缩文件,然后把压缩的文件流直接给输出流
    OutputStream outputStream = response.getOutputStream();
    ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);

    for (int i = 0,len = stu_nos.size(); i < len; i++) {

        //拿到该学生的记录
        Student student = studentPOJO.getStudentByStuNo(stu_nos.get(i));
        if (student == null)
            continue;
        //System.out.println(student.toString());
        ZipEntry zipEntryXtv = new ZipEntry((student.getStu_name() + "_"+student.getStu_no() +".pdf"));
        zipOutputStream.putNextEntry(zipEntryXtv);
        //将student转成map
        HashMap<String,Object> studentMap = (HashMap) TransBeanMap.transBeanToMap(student);
        try {
            //先用 baos 来接收写入的pdf文件,然后再将 baos 写到压缩流中
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            WriteContent pdf = new WriteContent(PageSize.A4,baos,excelDictionary);
            pdf.open();
            pdf.setTitle("大学信息工程学院学生信息");
            pdf.close();
            //写入压缩输出流
            zipOutputStream.write(baos.toByteArray());

        } catch (Exception e) {

        }
    }
    zipOutputStream.close();
    return null;


}


可以看见,我们这次是批量导出了学生pdf数据,而其中用到了 ZipOutputStream ,并且直接将 outputStream 给了他,很明显的意思是,我们希望压缩流直接输出到前端。那我们是不是往压缩流填东西就可以了吗。那压缩流里面填东西也不难,我们只需将每个pdf输出的流,转成字节数组,一个一个写入压缩流即可。这样我们就得到了一个多文件传输了。


总结:


怎么说呢,其实这就是io操作,一切都是通过 outputStream 来返给前端,所以不管我们需要传输任何文件,那你都需要转成流输出。


如果你不需要在服务器磁盘上读取文件,而是生成动态的请求文件,那么请你尽量不要在服务器生成文件后再读取返给前端。因为我们可以直接写出输出流。


如果你必须生成持久化文件存在服务器,那当然也没问题,不过io流最重要的东西别忘了。打开和关闭