記一次 OSS 大批次檔案下載的實現 → bat指令碼不好玩!

2023-11-06 12:02:31

開心一刻

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

  侄女一臉期待的看著我哥:爸爸,你說媽媽和奶奶誰漂亮啊?

  我哥不慌不忙的拿起一粒瓜子,輕聲說道:為啥沒有你啊?

  侄女笑容漸起,似乎得到了她想要的回答,仍繼續問道:那媽媽和奶奶還有我,誰漂亮?

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

  侄女笑臉瞬間一拉,死死地盯著我哥,幽怨地問道:那裡為啥要加我呀?

  我哥再次瞄了一眼侄女,繼續剝著瓜子說到:我不想讓你媽媽墊底!

  侄女斜眼瞟向我哥,臉上寫滿了憤怒

  然後按照如下步驟進行檔案下載

  但有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語句從入門到精通