file

上次在本系列的第十篇中介紹過了 File System Access API,而最近這一陣子 Chromium 系列瀏覽器又再次推出了極度讓人興奮的新功能:File Handling API(這個 API 新到連 MDN 上面都還沒有文章,所以我只好直接連到 WICG 的文件)。這個功能把 PWA 的功能又邁進了重大的一步:它讓 PWA 可以註冊成某種檔案類型的預設應用程式,使得我們點開指定的檔案類型時、自動就會打開 PWA 來開啟之,而且當然也可以註冊檔案類型的圖示。

目前這個功能還處於實驗階段,而且由於它是 File System Access API 的進一步延伸、目前它也是桌機版限定的;不過作為開發人員,我們已經可以透過在桌機的 Chromium 系列瀏覽器(Chrome 或 Edge)中打開 about://flags/#file-handling-api 選項(這個直接做成連結沒有用,請各位手動複製到網址列中即可直達選項)來體驗之。根據小道消息,這個功能應該會在近期的 Chromium 更新之中變成正式功能。

更新:從 Chrome 92 版開始此功能已經進入 origin trial,所以預設就是會開啟,無須再透過 flags。

不過因為我當然不可能忍著不去嘗試這個敲碗了不知道多久的新功能,我當然馬上就在 BPS 上頭實作了一遍;我在 Windows 和 Mac 上面都測試成功,而該設定上號稱在 Linux 以及 Chrome OS 上也都沒問題。底下就來分享相關的細節。

修改 manifest

第一個重點是要修改 PWA 的 manifest.json 檔案,在裡面加上file_handlers 的設定:

{
    ...
    "file_handlers": [
        // 這個陣列中每一項代表一種檔案類型
        {
            "action": "/", // 要用哪一個網址開啟這種檔案
            "name": "BP Studio Project", // 檔案類別的名稱
            "accept": {
                // MIME 以及對應的副檔名(字串或字串陣列)
                "application/bpstudio.project": ".bps"
            },
            "icons": [
                // 檔案圖示陣列
                {
                    "src": "assets/icon/icon-512.png",
                    "type": "image/png",
                    "sizes": "512x512"
                },
                ... // 更多不同尺寸的圖示
            ]
        },
        ... // 更多檔案
    ]
}

更新:雖然規格提案上面目前寫的是上面這樣,不過 Chromium 目前只實作了 actionaccept 兩項設定,所以目前 nameicons 兩項設定並不會真正發揮效果;在檔案總管裡面顯示出來的檔案類別名稱永遠就是 App 本身的名字、而檔案圖示也是直接使用 App 自己的圖示。這個需要等之後 Chromium 繼續實作。

開啟檔案的處理程式

一旦開啟了 #file-handling-api 實驗選項之後,window 物件就會多出一個 launchQueue 成員,我這邊簡單來給一個 TypeScript 的定義:

declare global {
    export const launchQueue: LaunchQueue;

    interface LaunchQueue {
        setConsumer(consumer: LaunchConsumer): void;
    }

    interface LaunchConsumer {
        (launchParams: LaunchParams): void;
    }

    interface LaunchParams {
        readonly files: readonly FileSystemFileHandle[];
    }
}

其中 FileSystemFileHandle 的定義可參見之前的第十篇。而程式碼的部份其實滿簡單的:

if('launchQueue' in window) {
    launchQueue.setConsumer(launchParams => {
        let files: readonly FileSystemFileHandle[] = launchParams.files;
        // 然後看你要怎麼處理 files;參見第十篇
    });
}

這一段程式碼要在啟動之後的哪一個時間點執行都沒關係,只要網頁第一次呼叫了 setConsumer 方法,瀏覽器就會把佇列的檔案傳給指定的回調函數。不過要注意一個小細節:如果 PWA 是直接打開而沒有任何佇列檔案,那麼傳入的回調函數是不會被調用的,所以請注意不要試圖等待回調函數執行完畢,因為它可能永遠不會被執行。

透過這個 API 直接開啟檔案拿到的 FileSystemFileHandle 會自帶讀取權限(跟透過開啟對話方塊開啟的檔案一樣),但是不會立刻就有存檔權限,所以如果要存檔的話還是會請使用者確定一次權限。

進行測試

做好了上述的修改並且發佈完新的 PWA 版本之後,就可以來測試了。因為 Chromium 是每隔一段時間才會檢查 manifest 的更新,所以各位可以解除既有的 PWA 安裝並重新安裝,應該會立刻就生效。我實際測試的結果,檔案圖示不知道為什麼可能要等一下才會在檔案總管理面生效,但是開啟的部份倒是馬上就可用了。

當使用者第一次試圖用 PWA 直接開啟檔案的時候,瀏覽器會向使用者確認「檔案處理常式」的權限:

file

這個使用者只要同意一次,未來就再也不用確認了。開發人員如果想要反覆測試這個權限同意流程,可以按下 PWA 右上角的三個點,選擇「應用程式資訊」然後把「檔案處理常式」設定為「詢問」即可(不過每次測試同意之後都得再次設定就是了)。

結論

這個新的 API 真的把 PWA 又往前跨出了一大步。當然它在規格還是有很多不完美之處,例如它要註冊的檔案格式是寫死在 manifest 裡面的,所以對於通用型的編輯器(如 VS Code)來說、要靠這種 API 就打天下還是有困難的。除此之外目前也辦法指定多重視窗開啟的行為;目前是直接點選檔案開啟總是會打開新的 PWA 實體且無法選擇,但是也許開發 App 的人會比較希望能夠直接在同一個 PWA 實體裡面繼續打開,而這點目前是做不到的(有另外一項新功能的提案 Service Worker Launch Event 試圖在處理這個問題,不過這個還在草案階段)。

無論如何,這都是一個可以繼續期待未來發展的新功能。未來若這個 API 有更多新的進展或修改,本篇也會持續跟著更新。


分享此頁至:
最後修改日期: 2021/07/23

留言

撰寫回覆或留言

發佈留言必須填寫的電子郵件地址不會公開。