天天看點

從零實作“百度網盤批量重命名”工具

從零實作“百度網盤批量重命名”工具

該圖檔由Roshan Bhatia在Pixabay上釋出

你好,我是看山。

本文收錄在Java 進階系列中。

工具的發明能夠節省體力,同時也可以減少重複勞動,軟體也是工具的一種。今天要說的是,引用 IT 技術,減少大量檔案重命名這種重複的勞動。

一直在用的存儲雲盤是百度網盤,裡面收集了大量檔案。各種資料、電子書,使用空間達到了 2500G。之前還清理過一些低質的書籍,結果使用工具導出發現,在待整理目錄中,居然有 1942 條電子書的記錄。是以想要整理一番,這麼多的檔案,一個一個點實在難用。

提出需求

書歸正傳,這麼多的檔案,命名格式千奇百怪,因為有一些資料是從别人的分享中儲存的,有的還會帶網址的,可見網站營運也是無所不用其極了。從網上找到一些批量改名的工具,大多是 Windows 版本的,而且是加字首或者字尾之類的,不太适用。

我想要的是,自己指定檔案名,然後批量執行。就相當于有一雙手,幫我在百度網盤中執行官方提供的重命名。之是以不用官方的重命名,是因為太難用,而且浪費時間(後面會具體說一下百度網盤的這個設計,也是可以借鑒的)。

想要實作自己的想法,需要有兩步:

以 Excel 格式導出所有檔案名

在導出的 Excel 檔案中,定義目标名稱,不需要改名的可以不用修改

導入整理後的檔案,檢查是否重命名成功

設計方案

根據需求,我們來設計方案。

找到關鍵接口

首先,我們需要能夠導出所有的檔案名。

百度網盤提供了網頁版、用戶端版,為了省時省事,我們使用網頁版檢查邏輯。打開控制台,發現進入目錄時會有一個/api/list的請求,如下圖:

從零實作“百度網盤批量重命名”工具

根據響應内容,我們可以看出來,這個接口可以擷取指定目錄的檔案清單。這個請求是 Get 請求,包含了好幾個參數,還不太請求參數的作用,先放過。

通常來說,簡單的網絡請求是通過 Cookie 鑒權,是以我們就無腦使用 Cookie 了。

接下來需要找到重命名的請求,同樣的,執行百度網盤提供的重命名即可,新增了哪些請求。如下圖:

從零實作“百度網盤批量重命名”工具

可以看到,這裡的重命名分為了兩步:

送出重命名任務,傳回任務 id

使用任務 id 查詢任務執行情況

這就是前面說的可借鑒的地方。對于百度網盤這種應用,雖然下載下傳限速被各種诟病,還有阿裡雲盤的強勢追擊,但是不得不說,百度網盤還是現在用的比較多的雲存儲工具。必須有針對性的優化,将某些二級功能異步任務化,比如重命名。

先通過一個請求建立重命名任務,任務建立成功傳回任務 ID。這個時候,百度網盤後端服務監聽新任務,如果後端壓力大,任務可以緩慢執行或不執行;

既然是異步任務化,用戶端(包括網頁或用戶端)需要檢查任務執行情況。任務執行情況根據約定可以有進行中或者完成,還可以有拒絕、失敗、過期等其他情況。

導出導入檔案

我們可以借助阿裡開源的 EasyExcel 導出 Excel 檔案(具體操作,可以檢視 寫檔案、寫的好看、填充檔案 三篇)。

這個時候需要定義導出檔案的内容,根據重命名的請求我們可以知道,我們需要檔案路徑、檔案的新名字,為了操作簡單,我們可以直接把原名也導出來。為了檢查網盤檔案是否有重複的,最好把檔案的摘要碼也導出來。

至此,我們的需求和方案都設計好了,下面就開始編碼。

開始編碼

開始編碼前,我們需要定義一下鑒權參數:cookie、bdstoken,再定義一個擴充參數 path,我們隻導出指定目錄的檔案清單。

定義基礎類

根據設計方案中的定義,我們先建立導出檔案的基礎類:

@Data
public class FileName {
    @ExcelProperty("路徑")
    private String path;
    @ExcelProperty("MD5")
    private String md5;
    @ExcelProperty("原名稱")
    private String originName;
    @ExcelProperty("新名稱")
    private String newName;
}
      

因為涉及到網絡請求,我們需要定義請求參數。請求有一些共同參數:

@Data
public abstract class BaseRequest {
    protected String channel = "chunlei";
    protected String web = "1";
    protected String appId = "250528";
    protected String bdstoken = "";
    protected String logid = "";
    protected String clienttype = "0";
}
      

檔案清單參數為:

@EqualsAndHashCode(callSuper = true)
@Data
public class FileListRequest extends BaseRequest {
    private String order = "name";
    private String desc = "0";
    private String showempty = "0";
    private int page = 1;
    private int num = 100;
    private String dir = "/";
    private String t = "";
}
      

重命名參數為:

@EqualsAndHashCode(callSuper = true)
@Data
public class FileRenameRequest extends BaseRequest {
    private String opera = "rename";
    private String async = "2";
    private String onnest = "fail";
}
      

我們還需要一個查詢任務狀态的參數:

@EqualsAndHashCode(callSuper = true)
@Data
public class TaskStatusRequest extends BaseRequest {
    private Long taskid;
}
      

定義請求類

前面有了基礎類和請求類,接下來我們定義請求接口,這些類就是模闆化的方法了,我們簡單看一下。如果想要擷取源碼,關注公号「看山的小屋」回複“java”擷取源碼。

先定義檔案清單請求方法:

private List<FileListItem> listFileCurrentPath(FileListRequest fileListRequest) {
    final String body = HttpRequest.get("https://pan.baidu.com/api/list")
            .form(fileListRequest.paramMap())
            .header(this.headers)
            .cookie(this.cookie)
            .execute()
            .body();
    final FileListResponse response = JSONUtil.toBean(body, FileListResponse.class);
    if (response.getErrno() == 0) {
        return response.getList();
    }
    return Collections.emptyList();
}
      

在定義檔案重命名請求方法:

private Long rename(FileRenameRequest fileRenameRequest, String params) {
    final String queryParam = HttpUtil.toParams(fileRenameRequest.paramMap());
    final HttpRequest httpRequest = HttpRequest.post("https://pan.baidu.com/api/filemanager?" + queryParam)
            .header(this.headers)
            .cookie(this.cookie)
            .body(params);
    final String body = httpRequest.execute().body();
    final FileRenameResponse response = JSONUtil.toBean(body, FileRenameResponse.class);
    if (response.getErrno() == 0) {
        return response.getTaskid();
    }
    return -1L;
}
      

最後定義檢查任務狀态請求方法:

private TaskStatusResponse queryTaskStatus(TaskStatusRequest taskStatusRequest, String params) {
    TaskStatusResponse response;
    final String queryParam = HttpUtil.toParams(taskStatusRequest.paramMap());
    do {
        final String body = HttpRequest.post("https://pan.baidu.com/share/taskquery?" + queryParam)
                .header(this.headers)
                .cookie(this.cookie)
                .body(params)
                .execute()
                .body();

        response = JSONUtil.toBean(body, TaskStatusResponse.class);
    } while (response.getErrno() != 0 || StringUtils.equalsAny(response.getStatus(), "running", "pending"));
    return response;
}
      

體驗更新

全部類定義完成後,我們可以直接在 IDE 中運作。但是,既然是工具,每次使用還得打開 IDE,是不是有些 low 了。為了更新體驗,我們可以打成 jar 包,使用的時候直接運作 jar 包就行了。可以借助 maven 插件maven-assembly-plugin實作,這個插件能夠把我們的源碼和三方庫都打在一個 jar 包中,這樣就是一個 FatJar 走天下了。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.3</version>
    <configuration>
        <appendAssemblyId>false</appendAssemblyId>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib/</classpathPrefix>
                <mainClass>cn.howardliu.effectjava.rename.TaskRunner</mainClass>
            </manifest>
        </archive>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>assembly</goal>
            </goals>
        </execution>
    </executions>
</plugin>
      

幹完,手工。

文末總結

本文從零開始實作制作一個網絡小工具,實作百度網盤檔案的批量重命名。這個工具是這類工具的一個代表,隻要是網絡應用,存在 http 請求,我們都可以通過這類方式實作網絡小工具。