写在前面

当我们谈下载时,要注意什么?IO?API?SDK?

一、基本知识

下载的过程就是 复制的过程,三个基本名词 Source File,Target File,传输工具

二、Source File

源文件,Java中常见3种源文件,

  • 资源路径-Classpath
  • 文件系统 - File System
  • 磁盘目录

2.1、资源路径

常见场景很多,包括各种模板、手册(Word,Excel,PDF…),相关静态文件…

这里一般通过两个对象即可操作资源文件,可参考这里,

springboot中,如何从资源文件加载文件

获取 输出、输入流,即可完成下载

2.2、文件系统

这里的场景是基于一个文件服务,目前很多的文件服务的选择
fastDfs、HDFS、GlusterFS、各种云盘、各种云的OSS对象存储、甚至某个超链接…等等都可定义为文件系统的存储方式

维基百科上,文件系统列表

文件系统的文件操作方法是基于 API,或者 超链接方式,即可

这里有可用的 SDK 或者 相关工具类封装…完成下载

类似如下,apache common

    public static void downloadWithApacheCommons(String url, String localFilename) {
   
        int CONNECT_TIMEOUT = 10000;
        int READ_TIMEOUT = 10000;
        try {
   
            FileUtils.copyURLToFile(new URL(url), new File(localFilename), CONNECT_TIMEOUT, READ_TIMEOUT);
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }

2.3、磁盘目录

这里需要注意,开发环境,测试环境。以及生产环境因系统不同,文件分割符会有所不同

is = new FileInputStream(filePath + "\\" + path.getFileName()); // windows
is = new FileInputStream(filePath + "/" + path.getFileName()); // linux 目录分隔符

JDK 中 File.separator 可自动识别系统分隔符,因此上述可替换为

is = new FileInputStream(filePath + File.separator + path.getFileName());

还要注意 ,Java 项目 和 Springboot 项目测试中的差别,即springboot 项目中的 所获取Path路径是相对路径,会把磁盘某文件真实路径,映射 当前项目下,导致无法获取真实文件路径,获取输入流失败,可参考上述,拼接文件的完整路径,获取输入流

三、Target File

这里是指期望下载文件到某位置,一般通过浏览器方式下载(通过输出流写到浏览器默认下载目录)

四、传输工具

这里是指 IO 流的 读写方式,包括 字符流、字节流

示例(指向磁盘目录下载)

这里完成了 指向目标目录,过滤文件,下载

// 接口定义
@GetMapping("/manual")
    public void manualDownload(HttpServletResponse response) {
   
        InputStream is = null;
        ServletOutputStream os = null;

        String filePath = configService.findDefaultFilePath();
        if (filePath == null) {
   
            throw new CustomeException("文件地址为空");
        }

        List<Path> results = new LinkedList<Path>();
        try {
   
            Files.walkFileTree(Paths.get(filePath), new FindJavaVisitor(results));
            if (results.size() != 1) {
   
                throw new CustomeException("文件异常" + results.size());
            }
            Path path = results.get(0);
            is = new FileInputStream(filePath + File.separator + path.getFileName());
            response.addHeader("charset", "utf-8");

            String encodeName = URLEncoder.encode(path.getFileName().toString(), StandardCharsets.UTF_8.toString());
            response.setHeader("Content-Disposition", "attachment; filename=\"" + encodeName + "\"; filename*=utf-8''" + encodeName);
            // copy (输入流,输出流)
            os = response.getOutputStream();
            IOUtils.copy(is, os);
            response.flushBuffer();
        } catch (Exception e) {
   
           throw new CustomeException("文件异常");
        } finally {
   
            try {
   
                if (os != null) {
   
                    os.close();
                }
                if (is != null) {
   
                    is.close();
                }
            } catch (Exception e) {
   
                e.printStackTrace();
            }
        }
    }

// 文件过滤
    private static class FindJavaVisitor extends SimpleFileVisitor<Path> {
   
        private List<Path> result;

        public FindJavaVisitor(List<Path> result) {
   
            this.result = result;
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
   
            if (file.toString().endsWith(".docx")) {
   
                result.add(file.getFileName());
            }
            return FileVisitResult.CONTINUE;
        }
    }