Java文件下载,提供前端页面下载、HttpClient接口下载

学习Java的文件上传/下载需要先明白一下几点:

1、下载的资源,有两种:

1)、本地文件:即项目运行时可访问的文件目录,比如,在本机Idea中运行一个 fileServer,那么 fileServer 可访问的你电脑目录下文件做为下载资源;或者 fileServer 在服务器上运行,就是服务器上可访问的目录下文件资源。

2)、远程文件:fileServer 运行的网络环境中 可访问的 Ftp/SFtp或其他服务接口中获取的文件字节码,I/O流等。

2、下载方式: 就是谁来获取这些“下载文件”,比如 :客户端的浏览器或这其他开发者通过接口获取你的文件。

总结:首先,以上两点,你需要知道, 第一,提供的接口中文件从哪里来,是本地文件还是远程文件 ;第二、文件提供给谁下载,你的返回类型是什么,是一个字节码、还是一个输出流。

一、提供“本地文件” ,给浏览器、前端Ajax或其他开发者接口来下载

1.1、controllert 类 

package com.appms.controller;

import com.appms.util.Util;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
@CrossOrigin("*")
@RequestMapping("file")
public class FileController {

    @GetMapping("/download")
    public void download(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Util.downloadFile(request, response);
    }
}

1.2、实现类 :Util.class


    public static void downloadFile(HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        // 本地文件地址,文件名称,我是在本机运行,如果是服务器的话,地址可能是 ../fileServer/file/deploy2.sh
        String filePath = "/Users/jjshen/bysj/deploy2.sh";
        String fileName = "deploy2.sh";
        
        
        // 获取浏览器的信息
        String agent = request.getHeader("USER-AGENT");
        if (agent != null && agent.toLowerCase().indexOf(FIRE_FOX) > 0) {
            //火狐浏览器自己会对URL进行一次URL转码所以区别处理
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes("GB2312"), "ISO-8859-1"));
        } else if (agent.toLowerCase().indexOf(SAFARI) > 0) {
            //苹果浏览器需要用ISO 而且文件名得用UTF-8
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
        } else {
            //其他的浏览器
            response.setHeader("Content-Disposition",
                    "attachment; filename=\"" + java.net.URLEncoder.encode(fileName, "UTF-8"));
        }

        File file = new File(filePath);
        FileInputStream fileInputStream = new FileInputStream(file);

        byte[] fileByte = new byte[(int) file.length()];
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024];
        int len;
        while ((len = fileInputStream.read(bytes, 0, bytes.length)) != -1) {
            byteArrayOutputStream.write(bytes, 0, len);
        }
        byteArrayOutputStream.close();
        fileByte = byteArrayOutputStream.toByteArray();

        OutputStream outputStream = null;
        outputStream = response.getOutputStream();
        outputStream.write(fileByte);
        outputStream.flush();
        outputStream.close();

    }

代码解释:文件下载是将文件流写入 HttpServletResponse中,在header中添加 浏览器的解析方式,和文件名:

Content-Disposition, attachment ; fileName = ? 这样如果在浏览器中直接访问接口,就可以直接下载了。如:浏览直接访问:http://127.0.0.1:9080/file/download,就可以在浏览器下载了。

1.3、前端页面调用

定义 responseType的类型为 “blob” ,然后通过 document 创建一个 a 标签,然后将文件名称、文件流设置进去即可。

    // 初始化一个HTTP请求
    const xhr = new XMLHttpRequest();
    xhr.open('GET', 'http://127.0.0.1:9080/file/download');
    xhr.responseType = 'blob';
    xhr.send();
    // 回调函数
    xhr.onload = function () {
        if (this.status === 200) { // HTTP 状态码
            const blob = this.response;
            const reader = new FileReader();
            reader.readAsDataURL(blob); // 读取指定的 Blob 或 File 对象
            // 处理读取事件
            reader.onload = function (e) {
                // 响应头存在中文需要编码
                const headerName = decodeURIComponent(escape(xhr.getResponseHeader('Content-disposition')));
                const headerNameArr = headerName.split('=');
                const fileName = headerNameArr[headerNameArr.length - 1];

                let aLink = document.createElement('a');
                aLink.download = fileName;
                aLink.href = e.target.result;
                aLink.click();
                $(aLink).remove();// 移除元素,但未清理DOM元素引用
                aLink = null; // 解除引用,内存释放
                alert('下载成功!');
            };

        } else {
            tools.errorAlerts('下载失败!');
        }
    };

1.4、其他服务接口调用:

调用接口,然后,根据 header中文件名称和 在返回reponse中的 文件流 , 转换成BufferedInputStream写入本地文件或用作其他。

    public static void main(String[] args) {
        String result = null;
        CloseableHttpClient httpClient = HttpClients.createDefault();

        HttpGet HttpGet = new HttpGet("http://127.0.0.1:9080/file/download");
//        HttpServletResponse response = null;
        HttpResponse response = null;
        try {
            response = httpClient.execute(HttpGet);
            // 获取header中文件名称
            String fileName = "";
            try {
                fileName = response.getAllHeaders()[3].getValue().split(";")[1].split("=")[1].substring(1);
                System.out.println(fileName);
            } catch (Exception e) {
            }
             // 后去接口返回的文件流
            HttpEntity entity = response.getEntity();
            BufferedInputStream br = new BufferedInputStream(entity.getContent());
            byte[] buf = new byte[1024];
            int len = 0;
            // 要写入本地的文件
            FileOutputStream fileOutputStream = new FileOutputStream("/Users/jjshen/bysj/" + fileName);
            while ((len = br.read(buf)) != -1) {
                fileOutputStream.write(buf, 0, len);
            }
            fileOutputStream.close();
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
//                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

二、提供“远程文件” ,给浏览器、前端Ajax或其他开发者接口来下载。

其实和 “第一点” 一样, 就是获取文件源的地方稍作修改。在“第一”点中是 直接 new File(filePath); 一个文件出来,转换成字节 流,放入 response中。

2.1、从 Ftp/SFtp上获取的

其实就是 使用 FTPClient 登陆到文件所在目录 ,然后将文件转换成一个 输出流OutputStream或Byte[]。

首先文件名要知道。ftp/sft的地址 登陆用户名/密码等。


    public static void downloadFile(HttpServletRequest request, HttpServletResponse response, String fileName)
            throws IOException {

        // 本地文件地址,文件名称,我是在本机运行,如果是服务器的话,地址可能是 ../fileServer/file/deploy2.sh
        //String filePath = "/Users/jjshen/bysj/deploy2.sh";
       // String fileName = "deploy2.sh";

        int reply; 
        FTPClient ftp = new FTPClient();          
        // 获取浏览器的信息
        String agent = request.getHeader("USER-AGENT");
        if (agent != null && agent.toLowerCase().indexOf(FIRE_FOX) > 0) {
            //火狐浏览器自己会对URL进行一次URL转码所以区别处理
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes("GB2312"), "ISO-8859-1"));
        } else if (agent.toLowerCase().indexOf(SAFARI) > 0) {
            //苹果浏览器需要用ISO 而且文件名得用UTF-8
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
        } else {
            //其他的浏览器
            response.setHeader("Content-Disposition",
                    "attachment; filename=\"" + java.net.URLEncoder.encode(fileName, "UTF-8"));
        }

     ftp.connect("http://xxx.xxx.xxx", 22);
     //下面三行代码必须要,而且不能改变编码格式
     ftp.setControlEncoding("GBK");
     ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
     FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);  
     conf.setServerLanguageCode("zh");
     //如果采用默认端口,可以使用ftp.connect(url) 的方式直接连接FTP服务器  
     ftp.login("root", "123456");//登录  
     reply = ftp.getReplyCode();  
     if (!FTPReply.isPositiveCompletion(reply)) {  
         ftp.disconnect();  
         return success;  
     }  
     String remotePath = "/home/username/files/";
 
     ftp.changeWorkingDirectory(remotePath+"/");//转移到FTP服务器目录  
     FTPFile[] fs = ftp.listFiles(); 

     OutputStream outputStream = response.getOutputStream();
     for(int i = 0; i < fs.length; i++){
         FTPFile ff = fs[i];
         if(ff.getName().equals(fileName)){ 
//                   System.out.println("fileName"+fileName);
             String filename1 = URLEncoder.encode(fileName,"utf-8");
             response.setHeader("Content-disposition","attachment;filename="+URLEncoder.encode(fileName,"utf-8"));
             //将文件保存到输出流outputStream中
             ftp.retrieveFile(new String(ff.getName().getBytes("GBK"),"ISO-8859-1"), outputStream);
             outputStream.flush();
             outputStream.close();
             break;
         }  
     }                     
     ftp.logout();  
     success = true;  
     ftp.disconnect();

    }

上面是使用 FtpClient.retrieveFile 直接将Ftp上文件写入response 的outputStream中。其实 FtpClient.retrieveFile 的效率没有 ftpClient.retrieveFileStream方法快。 如果考虑效率这里可以将retrieveFile 方法修改成 retrieveFileStream,然后再将返回结果写入 outputStream 中去。


    public static void downloadFile(HttpServletRequest request, HttpServletResponse response, String fileName)
            throws IOException {

        // 本地文件地址,文件名称,我是在本机运行,如果是服务器的话,地址可能是 ../fileServer/file/deploy2.sh
        //String filePath = "/Users/jjshen/bysj/deploy2.sh";
       // String fileName = "deploy2.sh";

        int reply; 
        FTPClient ftp = new FTPClient();          
        // 获取浏览器的信息
        String agent = request.getHeader("USER-AGENT");
        if (agent != null && agent.toLowerCase().indexOf(FIRE_FOX) > 0) {
            //火狐浏览器自己会对URL进行一次URL转码所以区别处理
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes("GB2312"), "ISO-8859-1"));
        } else if (agent.toLowerCase().indexOf(SAFARI) > 0) {
            //苹果浏览器需要用ISO 而且文件名得用UTF-8
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
        } else {
            //其他的浏览器
            response.setHeader("Content-Disposition",
                    "attachment; filename=\"" + java.net.URLEncoder.encode(fileName, "UTF-8"));
        }

     ftp.connect("http://xxx.xxx.xxx", 22);
     //下面三行代码必须要,而且不能改变编码格式
     ftp.setControlEncoding("GBK");
     ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
     FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);  
     conf.setServerLanguageCode("zh");
     //如果采用默认端口,可以使用ftp.connect(url) 的方式直接连接FTP服务器  
     ftp.login("root", "123456");//登录  
     reply = ftp.getReplyCode();  
     if (!FTPReply.isPositiveCompletion(reply)) {  
         ftp.disconnect();  
         return success;  
     }  
     String remotePath = "/home/username/files/";
 
     OutputStream outputStream= response.getOutputStream(); 
     ftp.changeWorkingDirectory(remotePath+"/");//转移到FTP服务器目录  
     InputStream   inputStream = ftpClient.retrieveFileStream(new String(fileName.getBytes("GBK"), "ISO-8859-1"));

    byte[] buf = new byte[1024];
    int len;
    while ((len = inputStream.read(buf, 0, buf.length)) > 0) {
        outputStream.write(buf, 0, len);
    }
    inputStream.close();

    outputStream.flush();
    outputStream.close();
                      
     ftp.logout();  
     ftp.disconnect();

    }

2.2、从接口中获取

其实就相当于 在1.4调用接口成功后,将文件流,在提供给返回给浏览器、前端页面、接口等。

------------------------------------------------------分割线---------------------------------------------------------------------------

博客只作为博主记录、复习用,如果有什么不足之处或更好的建议,都可以一起畅快的交流。对你有用或拿来参考的话,请自行完善,有任何不足或引发的事故概不负责。

  • 6
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值