• Java实现FTP下载文件到客户端(浏览器)


    一、简介

      我在之前的文章(Java实现文件上传和下载)里讲过非FTP文件的上传和下载,也讲过Java实现FTP文件上传和下载。本文主要讲通过FTP下载文件到浏览器,本文测试过程中Spring Boot 版本为2.6.0commons-net 版本为3.8.0,JDK环境为 1.8

    二、maven依赖

    pom.xml

    
    <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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>2.6.0version>
            <relativePath/> 
        parent>
    
        <groupId>com.aliangroupId>
        <artifactId>ftpartifactId>
        <version>0.0.1-SNAPSHOTversion>
        <name>ftpname>
        <description>java实现FTP下载文件到浏览器description>
    
        <properties>
            <java.version>1.8java.version>
        properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
    
            <dependency>
                <groupId>org.apache.commonsgroupId>
                <artifactId>commons-lang3artifactId>
                <version>3.12.0version>
            dependency>
    
            <dependency>
                <groupId>commons-netgroupId>
                <artifactId>commons-netartifactId>
                <version>3.8.0version>
            dependency>
    
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <version>1.16.14version>
            dependency>
    
            <dependency>
                <groupId>junitgroupId>
                <artifactId>junitartifactId>
                <version>4.12version>
                <scope>compilescope>
            dependency>
    
        dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                plugin>
            plugins>
        build>
    
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    三、配置类

    AppProperties.java

    package com.alian.ftp.config;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    @Data
    @Component
    @ConfigurationProperties(prefix = "ftp")
    public class AppProperties {
    
        /**
         * ftp主机地址
         */
        private String hostName;
    
        /**
         * ftp端口
         */
        private int port;
    
        /**
         * ftp用户名
         */
        private String userName;
    
        /**
         * ftp密码
         */
        private String password;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    相关的属性配置如下:

    application.yml

    server:
      port: 8081
      servlet:
        context-path: /ftp
    
    ftp:
      host-name: 192.168.0.151
      port: 21
      user-name: alian
      password: alian012
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    四、工具类

    ApacheFtpUtil.java

    package com.alian.ftp.utils;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.commons.lang3.tuple.Pair;
    import org.apache.commons.net.ftp.FTP;
    import org.apache.commons.net.ftp.FTPClient;
    import org.apache.commons.net.ftp.FTPFile;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.net.URLEncoder;
    import java.nio.charset.StandardCharsets;
    
    @Slf4j
    public class ApacheFtpUtil {
    
        private FTPClient ftpClient;
    
        /**
         * 实例化
         *
         * @param hostName FTP服务器地址
         * @param port     FTP服务器端口
         * @param userName FTP登录账户
         * @param password FTP登录密码
         * @throws IOException
         */
        public ApacheFtpUtil(String hostName, int port, String userName, String password) throws IOException {
            ftpClient = new FTPClient();
            //设置传输命令的超时
            ftpClient.setDefaultTimeout(20000);//毫秒
            //设置两个服务连接超时时间
            ftpClient.setConnectTimeout(10000);//毫秒
            //被动模式下设置数据传输的超时时间
            ftpClient.setDataTimeout(15000);//毫秒
            //被动模式
            ftpClient.enterLocalPassiveMode();
            //连接FTP
            ftpClient.connect(hostName, port);
            //更加账户密码登录服务
            ftpClient.login(userName, password);
        }
    
        public Pair<Boolean, String> downloadFile(String ftpFilePath, String fileName, HttpServletResponse response) {
            InputStream input = null;
            OutputStream out = null;
            try {
                response.reset();
                //此处你就根据你要下载的类型去设置即可,比如我下载.avi格式的文件,可以设置response.setContentType("video/avi");
                response.setContentType("application/download");
                //解决中文不能生成文件
                response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName,"UTF-8"));
                //传输模式
                ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
                // 设置以二进制流的方式传输
                ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
                //进入目录
                ftpClient.changeWorkingDirectory(ftpFilePath);
                FTPFile[] files = ftpClient.listFiles();
                if (files.length < 1) {
                    return Pair.of(false, "目录为空");
                }
                boolean fileExist = false;
                boolean downloadFlag = false;
                for (FTPFile ftpFile : files) {
                    String ftpFileName=new String(ftpFile.getName().getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
                    if (fileName.equals(ftpFileName)) {
                        fileExist = true;
                        input = ftpClient.retrieveFileStream(new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
                        out = response.getOutputStream();
                        int len;
                        byte[] bytes = new byte[1024];
                        while ((len = input.read(bytes)) != -1) {
                            out.write(bytes, 0, len);
                        }
                        out.flush();
                        downloadFlag = true;
                        break;
                    }
                }
                if (!fileExist) {
                    return Pair.of(false, "FTP服务器上文件不存在");
                }
                return Pair.of(downloadFlag, downloadFlag ? "下载成功" : "下载失败");
            } catch (IOException e) {
                e.printStackTrace();
                return Pair.of(false, "下载文件异常");
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                    if (input != null) {
                        input.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void close() {
            try {
                if (ftpClient != null && ftpClient.isConnected()) {
                    ftpClient.disconnect();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

      大致的流程:

    • 设置ContentTypeHeader
    • 设置二进制流的传输模式
    • 进入到要下载的文件的目录,获取到文件列表进行遍历
    • 如果文件存在,则通过流的方式进行文件的读写
    • 输出流刷到页面(依赖Header中的设置)

    4.1、服务器文件名中文处理

      关于中文文件名编码说明:

     //设置编码(支持UTF-8字符集的操作系统)
     ftpClient.setAutodetectUTF8(true);
    
    • 1
    • 2

      有些小伙伴可能会去设置上面这个代码,这个就是在支持UTF-8字符集的操作系统里会自动处理中文,比如这个apache文件服务器上文件一般默认是ISO-8859-1编码,使用它就会自动变成UTF-8。但是服务器可能不支持UTF-8字符集,ftpClient.setAutodetectUTF8(true)不会生效,又会有问题,所有建议不加,那就使用下面的代码进行编码转换了。

    String ftpFileName=new String(ftpFile.getName().getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
    
    • 1

    4.2、下载文件名中文处理

      假设下载的文件的文件名有中文时,我们就需要进行URLEncoder,下面这个就是对中文的处理,同时可以重命名文件

    response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName,"UTF-8"));
    
    • 1

    五、接口验证

    FtpController.java

    package com.alian.ftp.controller;
    
    import com.alian.ftp.config.AppProperties;
    import com.alian.ftp.utils.ApacheFtpClient;
    import com.alian.ftp.utils.ApacheFtpUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.tuple.Pair;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @Slf4j
    @Controller
    @RequestMapping(value = "/file")
    public class FtpController {
    
        @Autowired
        private AppProperties appProperties;
    
        @RequestMapping("download")
        public String downloadFtpFile(HttpServletRequest request, HttpServletResponse response) {
            ApacheFtpUtil ftpUtil = null;
            try {
                //http://localhost:8081/ftp/file/download
                String ftpFilePath="/icss/a2ce09b3536e57de";
                String fileName="20220803下载的视频.avi";
                log.info("文件路径:{}",ftpFilePath);
                log.info("文件名称:{}",fileName);
                ftpUtil = new ApacheFtpUtil(appProperties.getHostName(), appProperties.getPort(), appProperties.getUserName(), appProperties.getPassword());
                Pair<Boolean, String> pair = ftpUtil.downloadFile(ftpFilePath, fileName, response);
                return pair.getRight();
            } catch (Exception e) {
                log.error("下载异常",e);
                return "下载文件异常";
            } finally {
                if (ftpUtil != null) {
                    ftpUtil.close();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

      请求地址:http://localhost:8081/ftp/file/download

    在这里插入图片描述

  • 相关阅读:
    C++ Reference: Standard C++ Library reference: C Library: cctype: isdigit
    HT32基本外设使用
    国际经济学 简答计算
    (2022版)一套教程搞定k8s安装到实战 | ConfiMap
    企业征信牌照9月末盘点:149家机构荣获上榜,西藏等地机构待批
    【JavaEE初阶】线程的概念与创建
    部署LVS集群之DR模式
    el-autocomplete 必填校验问题
    如何在idea中创建一个SpringBoot项目(超详细教学)
    接口测试快速入门 以飞致云平台为例
  • 原文地址:https://blog.csdn.net/Alian_1223/article/details/126143386