记一次 OSS 大批量文件下载的实现 → bat脚本不好玩!

开心一刻

一天夜里,侄女跟我哥聊天

侄女一脸期待的看着我哥:爸爸,你说妈妈和奶奶谁漂亮啊?

我哥不慌不忙的拿起一粒瓜子,轻声说道:为啥没有你啊?

侄女笑容渐起,似乎得到了她想要的回答,仍继续问道:那妈妈和奶奶还有我,谁漂亮?

我哥瞄了一眼侄女,又拿起一粒瓜子坚定的说到:奶奶!

侄女笑脸瞬间一拉,死死地盯着我哥,幽怨地问道:那里为啥要加我呀?

我哥再次瞄了一眼侄女,继续剥着瓜子说到:我不想让你妈妈垫底!

侄女斜眼瞟向我哥,脸上写满了愤怒

一旁的我,肚子笑疼了!

背景需求

背景

项目基于 SpringBoot ,采用 B/S 模式

主要功能是生成文件,然后将文件上传都 OSS

目前通过浏览器可以下载少量文件,但一旦文件过多或文件过大(总体来说就是过大),浏览器下载会很慢,极其容易超时而失败

需求

而最近频繁接到产品那边的反馈,说是要下载最近几个月,甚至两三年的某些文件

总之就是要支持大批量文件的下载

大批量下载实现

既然业务部门发话了,作为支撑部门,必须得全力以赴,那就整呗

阿里云oss提供了三种下载文件的方式:简单下载断点续传下载授权给第三方下载

我们只看简单下载,其他两种方式大家自行去了解

简单下载方式也分好几种,我们一个个来看

OSS控制台

OSS控制台指的就是

然后按照如下步骤进行文件下载

但有2点需要注意

1、不支持通过OSS控制台下载文件夹(包含子目录)

如果需要下载文件夹(包含子目录),您可以使用ossbrowser、ossutil、SDK、API等方式进行下载

2、通过OSS控制台可一次批量下载最多100个文件

最多只能下载100个文件,多不了一点

很显然这种方式不合适

1、文件上传到OSS的路径规则导致每个文件都在不同的文件夹下,结果就是只能一个一个文件下载

效率太低,谁操作谁得骂娘

2、批量下载的文件数据很容易超过100

图形化管理工具ossbrowser

关于 ossbrowser 不做过多介绍,大家直接查阅官网即可:图形化管理工具ossbrowser

简单点来说, ossbrowser 就是 OSS控制台 的 C 端

所以也不合适,不合适点和 OSS控制台 方式一样

阿里云SDK

目前已实现的少量文件的下载,就是通过此种方式实现的

产品通过浏览器请求后端,后端通过 阿里云SDK 将文件下载到后端服务器,下载完成之后进行打包,最后将打包文件以流的方式通过浏览器保存到产品电脑的指定文件夹下

一旦打包文件很大,通过浏览器就很容易超时失败

那就不通过浏览器呗,后端将打包文件上传到指定的FTP服务器的指定目录下,然后产品去FTP服务器上的指定目录下拿打包文件

看似可行,但还是有一些不容忽视的不足

1、需要一台FTP服务器,而且需要对这台FTP服务器进行维护

2、产品需要从FTP拿打包文件,如果打包文件很大,这个复制过程也很费时

所以这种方式也不是很合适

命令行工具ossutil

关于 ossutil ,命令行工具ossutil已经讲的很详细了

使用 ossutil 之前,需要先配置它,大家按照配置ossutil配置即可

需要强调的一点是,如果仅仅只是下载,那么配置的时候用 bucket 的只读账号即可

本次下载只用到了lscp两个命令,我们来看下这两个命令的使用

ossutil64 ls oss://qsl-yzb-test/UserData/9088/20230920

ossutil64 cp oss://qsl-yzb-test/UserData/9088/20230920/Snipaste_2023-09-25_16-24-39.png D:\qsl-yzb-test\20230920\

我们发现,文件已经下载到 D:\qsl-yzb-test\20230920 目录下

感觉跟需求很吻合,如果能从单个下载改成批量下载,那么需求就实现了

一次输入一个 CMD 命令,显然是不行的,需要以 bat 脚本的方式实现多命令的执行,完成文件的下载

假设我们要下载 1011、9088、9999 这三个资源202308、202309两个月的文件, bat 脚本该如何写?

我们用两个配置文件来配置资源和月份,类似如下

bat 的 for 很强大,尤其以 for /f 最强,格式如下

分别对应文件字符串命令

我们先用 for /f 来读取两个配置文件

执行结果如下

有 2 点需要注意

1、 cmd 下,变量用一个 % 来表示,比如 %r,示例:

但是批处理(bat脚本)时,变量需要 %% 来表示,比如 %%r

2、for命令的形式变量只能是26个字母中的任意一个,同时区分大小写

配置文件的解析已经实现,接下来需要结合 ossutil 的命令来实现文件的下载了

一步一步来,我们先结合 ossutil 的 ls 命令获取文件列表

ossutil64 ls oss://qsl-yzb-test/UserData/9999/202309 结果如下

我们关注的其实只是 ObjectName 那一列,而 for /f 正好能实现

for /f "tokens=8 delims= " %p in ('ossutil64 ls oss://qsl-yzb-test/UserData/9999/202309') do echo %p

效果如下

delims=符号列表 :以指定符号列表对字符串进行切割,如果没有指定 delims ,那么默认则以空格键或跳格键作为分隔符号

for /f "delims= " 和 for /f 是一样的效果

tokens=n :定点提取第 n 个字符串

tokens 后可以接多个数字,以逗号隔开,例如: tokens=2,5,8

delims 进行切割, tokens 获取切割后指定位置的字符串

放进 bat 脚本

执行结果如下

我们需要的是文件列表,不需要关注目录,那如何过滤掉目录了?

ossutil 的 ls 命令正好有 --include 参数能实现过滤

执行结果如下

oss文件路径已经获取到,接下来就是结合 cp 命令进行下载了

执行后, D:\qsl-yzb-test 目录下文件如下

离成功还差一步之遥,需要将文件按日期进行划分,比如 20230921 这天的所有文件全部放到 20230921 这个目录下

oss文件路径是有规则的,具体文件名的上一级目录就是日期目录,所以我们可以从oss路径中截取日期目录, for /f 正好能实现

执行后, D:\qsl-yzb-test 目录内容如下

自此,算是大功告成了

但如果能手动指定下载目录就好了(下载目录作为 bat 参数)

这个很简单,直接上代码

完整脚本代码

复制代码
@ECHO OFF
rem 字符编码设置成UTF-8编码,防止中文乱码
chcp 65001

rem %1 下载目录
if "%1"=="" (
    echo "请指定下载目录,类似 D:\qsl-yzb-test"
    goto :eof
)

for /f %%r in (resource_config.txt) do (
    for /f %%m in (month_config.txt) do (
        for /f "tokens=8 delims= " %%p in ('ossutil64 ls oss://qsl-yzb-test/UserData/%%r/%%m --include *.*') do (
            for /f "tokens=5 delims=/" %%d in ("%%p") do (
                rem -f表示同名覆盖
                ossutil64 cp %%p %1\%%d\ -f
            )
        )
    )
)

View Code

REST API

自定义要求较高的情况可以考虑这种方式,感兴趣的可以去看官方说明

总结

1、 ossutil 提供了很多命令,实现需求之前可以先翻一翻官方文档说明

2、 cmd 和 bat 的变量命名是有区别的,大家一定要注意

3、 for 很强大, for /f 强大的最突出

参考

批处理for语句从入门到精通