java--文件下载

  • 总览
  • 单个文件下载
  • 多文件下载,并生成压缩包
  • 遇到的问题


总览

因为公司使用了文件服务器,所以记录一下文件的下载,一个是单个文件下载。在单个文件下载下形成多个文件以压缩包的形式下载。

单个文件下载

流程是从文件服务器上下载文件到项目环境上,然后再传输给前端浏览器:

public void downLoadFile() {
		//项目环境下的request和response
        HttpServletRequest request = (HttpServletRequest) RpcContext.getContext().getRequest();
        HttpServletResponse response = (HttpServletResponse) RpcContext.getContext().getResponse();
        String fileName = request.getParameter("fileName");
        //用于识别下载文件的类型,主要是环境中配置了不同文件不同路径。
        String fileType = request.getParameter("flag");
        LOGGER.info("文件的类型!"+fileType);
        LOGGER.info("文件的名称!"+fileName);
        String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
        LOGGER.info("文件下载接口最开始入参为:fileName="+fileName);
        //根据不同的文件类型获取到不同的文件服务器地址目录
        String serverPath = getServerPath(fileType);
        //根据不同的文件类型获取到不同的本地目录
        String localPath = getLocalPath(fileType);
        try {
        	//文件服务器目录
            String remotePath = basePath + serverPath;
            FTPUtils ftpUtils = new FTPUtils();
            //下载图片到服务器
            //ftpIp为fip地址
            boolean downloadFile = ftpUtils.downloadFile(ftpIp, port, username, password, remotePath, fileName, localPath);
            if(downloadFile){
                LOGGER.info("下载到本地服务器成功!");
                String path = localPath +"/"+ fileName;
                LOGGER.info("在服务器的路径为:"+path);
                String downLoadName = fileName.substring(0, fileName.indexOf("."));
                downLoadName = URLEncoder.encode(downLoadName, "UTF-8");
                LOGGER.info("转码后的名称为:"+downLoadName);
                LOGGER.info("设置响应头,控制浏览器下载该文件!");
                response.setHeader("content-disposition", "attachment;filename=" +(downLoadName + fileSuffix));
                LOGGER.info("响应头成功");
                // 读取要下载的文件,保存到文件输入流
                FileInputStream in = new FileInputStream(path);
                LOGGER.info("图片输入流为:"+in);
                // 创建输出流
                LOGGER.info("创建输出流!");
                OutputStream out = response.getOutputStream();
                // 创建缓冲区
                byte buffer[] = new byte[1024];
                int len = 0;
                // 循环将输入流中的内容读取到缓冲区当中
                LOGGER.info("开始输出!");
                while ((len = in.read(buffer)) > 0) {
                    // 输出缓冲区的内容到浏览器,实现文件下载
                    out.write(buffer, 0, len);
                }
                LOGGER.info("输出结束!");
                // 关闭文件输入流
                in.close();
                // 关闭输出流
                out.close();
                File localFile = new File(localPath + File.separatorChar + fileName);
                boolean flag = localFile.delete();
                if(flag){
                    LOGGER.info("传输完成,成功删除文件:{}",fileName);
                    out.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("文件下载接口异常为:"+ e.getMessage());
        }
    }
  1. 其中ftpUtils类中单文件和多文件下载区别并不大,下载文件方法为:
public class FTPUtils {
/**
     * Description: 从FTP服务器下载文件
     *
     * @param host       FTP服务器hostname
     * @param port       FTP服务器端口
     * @param username   FTP登录账号
     * @param password   FTP登录密码
     * @param remotePath FTP服务器上的相对路径
     * @param fileName   要下载的文件名
     * @param localPath  下载后保存到本地的路径
     * @return
     */
    public boolean downloadFile(String host, int port, String username, String password, String remotePath,
                                String fileName, String localPath) {
        boolean result = false;
        FTPClient ftp = new FTPClient();
        try {
            int reply;
            ftp.connect(host, port);
            // 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
            ftp.login(username, password);// 登录
            reply = ftp.getReplyCode();
            LOGGER.info("FTP登录成功!");
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftp.disconnect();
                LOGGER.error("下载文件异常,ftp服务连接失败!");
                return result;
            }
            if (FTPReply.isPositiveCompletion(ftp.sendCommand("OPTS UTF8", "ON"))) {
                LOCAL_CHARSET = "UTF-8";
            }
            ftp.setControlEncoding(LOCAL_CHARSET);
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
            ftp.enterLocalPassiveMode();
            ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
            FTPFile[] fs = ftp.listFiles();
            LOGGER.info("FTP目录上文件个数====================="+fs.length);
            File file = new File(localPath);
            if (!file.exists()) { // 判断文件目录存在不,不存在则创建
                file.mkdirs();
            }
            LOGGER.info("localPath================="+localPath);
            for (FTPFile ff : fs) {
                if (ff.getName().equals(fileName)) {
                    LOGGER.info("ff.getName================="+ff.getName());
                    LOGGER.info("fileName================="+fileName);
                    File localFile = new File(localPath + "/" + ff.getName());
                    LOGGER.info(localPath+ "/" +ff.getName());
                    if (!localFile.exists()) { // 判断文件存在不,不存在则创建
                        localFile.createNewFile();
                    }
                    LOGGER.info("将ftp上图片转为流!");
                    OutputStream is = new FileOutputStream(localFile);
                    //ftp.retrieveFile(ff.getName(), is);
                    LOGGER.info("下载图片到服务器成功!");
                    String ffp=new String(ff.getName().getBytes(LOCAL_CHARSET),SERVER_CHARSET);
                    ftp.retrieveFile(ffp , is);
                    LOGGER.info("下载的fileName!"+ffp);
                    is.close();
                    break;
                }
            }
            ftp.logout();
            LOGGER.info("下载成功!");
            result = true;
        } catch (IOException e) {
            LOGGER.error("下载文件异常{}", e);
        } finally {
            if (ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException ioe) {
                }
            }
        }
        return result;
    }
}

/**
     * Description: 从FTP服务器下载文件
     *
     * @param host       FTP服务器hostname
     * @param port       FTP服务器端口
     * @param username   FTP登录账号
     * @param password   FTP登录密码
     * @param remotePath FTP服务器上的相对路径
     * @param fileName   要下载的文件名,为一个List
     * @param localPath  下载后保存到本地的路径
     * @return
     */
    public boolean downloadFileList(String host, int port, String username, String password, String remotePath,
                                String[] fileName, String localPath) {
        boolean result = false;
        FTPClient ftp = new FTPClient();
        try {
            int reply;
            ftp.connect(host, port);
            // 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
            ftp.login(username, password);// 登录
            reply = ftp.getReplyCode();
            LOGGER.info("FTP登录成功!");
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftp.disconnect();
                LOGGER.error("下载文件异常,ftp服务连接失败!");
                return result;
            }
            if (FTPReply.isPositiveCompletion(ftp.sendCommand("OPTS UTF8", "ON"))) {
                LOCAL_CHARSET = "UTF-8";
            }
            ftp.setControlEncoding(LOCAL_CHARSET);
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
            ftp.enterLocalPassiveMode();
            ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
            FTPFile[] fs = ftp.listFiles();
            LOGGER.info("FTP目录上文件个数====================="+fs.length);
            File file = new File(localPath);
            if (!file.exists()) { // 判断文件目录存在不,不存在则创建
                file.mkdirs();
            }
            LOGGER.info("localPath================="+localPath);
            for (FTPFile ff : fs) {
                for (String filename: fileName ) {
                    if (ff.getName().equals(filename)) {
                        LOGGER.info("ff.getName================="+ff.getName());
                        LOGGER.info("fileName================="+filename);
                        File localFile = new File(localPath + "/" + ff.getName());
                        LOGGER.info(localPath+ "/" +ff.getName());
                        if (!localFile.exists()) { // 判断文件存在不,不存在则创建
                            localFile.createNewFile();
                        }
                        LOGGER.info("将ftp上图片转为流!");
                        OutputStream is = new FileOutputStream(localFile);
                        //ftp.retrieveFile(ff.getName(), is);
                        LOGGER.info("下载图片到服务器成功!");
                        String ffp=new String(ff.getName().getBytes(LOCAL_CHARSET),SERVER_CHARSET);
                        ftp.retrieveFile(ffp , is);
                        LOGGER.info("下载的fileName!"+ffp);
                        is.close();
                        break;
                    }
                }

            }
            ftp.logout();
            LOGGER.info("下载成功!");
            result = true;
        } catch (IOException e) {
            LOGGER.error("下载文件异常{}", e);
        } finally {
            if (ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException ioe) {
                }
            }
        }
        return result;
    }

多文件下载,并生成压缩包

@GET
    @Path("downLoadFileList")
    public void batchDownload(@Context HttpServletRequest request, @Context HttpServletResponse response) {
        String fileName = request.getParameter("fileName");
        String[] fileNames = fileName.split(",");
        String fileType = request.getParameter("flag");
        List<File> files = new ArrayList<>();
        String serverPath = getServerPath(fileType);
        //项目服务器存储地址
        String localPath = getLocalPath(fileType);
        LOGGER.info("循环下载多个文件的个数:fileSize="+ fileNames.length);
//          String fileSuffix = filename.substring(filename.lastIndexOf("."));
//          LOGGER.info("文件下载接口最开始入参为:fileName="+filename);
        try {
            //FTP服务器上的相对路径
            String remotePath = basePath + serverPath;
            FTPUtils ftpUtils = new FTPUtils();
            //下载图片到服务器
            boolean downloadFile = ftpUtils.downloadFileList(ftpIp, port, username, password, remotePath, fileNames, localPath);
            if(downloadFile){
                for (String name: fileNames ) {
                    LOGGER.info("文件下载到本地服务器成功!");
                    String path = localPath +"/"+ name;
                    LOGGER.info("文件在服务器的路径为:"+path);
                    //多个文件
                    files.add(new File(path));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        File zipPath = new File(localPath+"/"+(int)((Math.random()*9+1)*1000));
        if (!zipPath.exists()) { // 判断文件目录存在不,不存在则创建
            try {
                zipPath.createNewFile();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        zipFiles(request, response,files,zipPath);

    }
 /**
 	 *压缩文件并输出到response响应头中
     * @param response
     * @param fileList 多文件列表
     * @param zipPath 压缩的文件暂存的目录,下载后会删除掉
     */
    public static void zipFiles(HttpServletRequest request,HttpServletResponse response, List<File> fileList, File zipPath) {
        // 1 文件压缩

        FileOutputStream fileOutputStream=null;
        ZipOutputStream zipOutputStream=null;
        FileInputStream fileInputStream=null;
        try {
            LOGGER.info("文件本地服务器目录!");
            fileOutputStream=new FileOutputStream(zipPath); // 实例化 FileOutputStream对象
            zipOutputStream=new ZipOutputStream(fileOutputStream); // 实例化 ZipOutputStream对象
            ZipEntry zipEntry=null; // 创建 ZipEntry对象
            LOGGER.info("文件压缩个数:"+fileList.size());
            for (int i=0; i<fileList.size(); i++) { // 遍历源文件数组
                fileInputStream = new FileInputStream(fileList.get(i)); // 将源文件数组中的当前文件读入FileInputStream流中
                zipEntry = new ZipEntry(fileList.get(i).getName()); // 实例化ZipEntry对象,源文件数组中的当前文件
                zipOutputStream.putNextEntry(zipEntry);
                int len; // 该变量记录每次真正读的字节个数
                byte[] buffer=new byte[1024]; // 定义每次读取的字节数组
                while ((len=fileInputStream.read(buffer)) > 0) {
                    zipOutputStream.write(buffer, 0, len);
                }
            }
            zipOutputStream.closeEntry();
            zipOutputStream.close();
            fileInputStream.close();
            fileOutputStream.close();

            // 2 文件下载
            long currentTime=System.currentTimeMillis(); // 当时时间戳
            int randomFour=(int)((Math.random()*9+1)*1000); // 4位随机数
            String fileName=String.valueOf(currentTime)+String.valueOf(randomFour)+".zip"; // 新的文件名称
            String path=zipPath.toString();

            // 设置输出的格式
            response.setHeader("Content-Disposition", "attachment;filename=\""+ encodeFilename(request,fileName)+"\"");
            LOGGER.info("压缩文件名:"+fileName);
            response.setContentType("application/x-download");//告知浏览器下载文件,而不是直接打开,浏览器默认为打开
                // 循环取出流中的数据
            FileInputStream inStream=new FileInputStream(path); // 读到流中
            byte[] b = new byte[100];
            int len;
            try {
                OutputStream os=response.getOutputStream();
                while ((len = inStream.read(b)) > 0){
                    os.write(b, 0, len);
                }
                inStream.close();
                os.flush();
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {  // 3 删除压缩包
                String path=zipPath.toString();
                File zfile = new File(path);
                zfile.delete();
                for (File file:fileList){
                   file.delete();
                }
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }

遇到的问题

在测试的时候,文件名的传输顺序要在前面,不然会访问不到。