有道雲筆記非會員上傳圖片
背景
先看一下目前的效果:在「Markdown 筆記」原有的上傳圖片彈窗中增加了一個我們自定義的上傳按鈕,通過直接與後端 API 交互完成圖片上傳(相關 API 是「筆記」上傳時公開使用的)。
兩年前還沒開始使用 GitHub 記錄讀書筆記,那時在用有道雲筆記。我使用的是 「Markdown 筆記」,在最開始一段時間沒有上傳圖片的需求,所以用起來還可以。後來開始記錄《Head First 設計模式》的讀書筆記,並畫了每個模式的類圖,開始有了上傳圖片的需求,而官方將 「Markdown 筆記」上傳圖片的功能僅對會員開放。
窮則思變,機智的我就注意到了以前使用「筆記」時可以直接上傳圖片,並且沒有會員限制,將這個圖片的鏈接放到「Markdown 筆記」內也可正常使用。所以就先人工操作,每次需要上傳圖片時,先切到一個自己建立的用於上傳圖片的「筆記」,圖片上傳成功後再將鏈接拷貝回「Markdown 筆記」,暫時解決了上傳圖片的需求。
懶是第一生產力,做程序員最大的好處就是可以通過寫代碼簡化日常網上活動的各種重複性操作。在按照前面的方式完成幾篇帶圖片的「Markdown 筆記」後,就開始感到厭煩,這一操作機械重複沒有任何價值,所以就想到很適合通過代碼自動執行。
實現流程
由於我們需要的圖片上傳功能在「Markdown 筆記」頁面中沒有,所以不能使用操作頁面元素的方式,只能通過抓 API ,並且自己調用 API 來實現圖片上傳。
封裝 API
封裝 API 前我們需要抓 API ,這個很簡單,其實就是觸發一下我們所需要實現的功能,然後查看瀏覽器發送了哪些請求,記住這些請求並封裝一下,以便後續調用。
「筆記」圖片上傳操作後會發現瀏覽器發送了三個請求:
- 第一個是獲取
transmitId
以供後續兩個請求使用 - 第二個是使用
transmitId
上傳圖片(這裡僅實現了小文件單次上傳) - 第三個是使用
transmitId
給上傳完成的文件添加各種信息,並獲取圖片地址
一個簡單的圖片上傳只需要三個請求,所以我們先封裝一下,具體實現可以在 api.js 找到(其中還封裝了其他 API ,不過後續沒有使用到)
封裝上傳組件
點擊完上傳後,我們需要一個組件來實現選擇圖片、上傳圖片、返回圖片地址這三個操作。我們在前面封裝的 API 已經實現了上傳圖片並返回圖片地址的功能,所以在這裡我們這個組件只需要能觸發選擇圖片邏輯即可。我們可以通過 <input type="file">
來實現選擇文件的功能,然後我們需要對其註冊 change
事件,用於當用戶選擇完圖片後,實現後續的操作邏輯。
// 上傳文件,觸發後,會選擇文件,並執行上傳文件獲取url,最後執行回調(回調的第一個參數是文件,第二個參數是上傳的url)
function upload(accept, callback) {
// 1. 創建 input 節點
if($('#diy-uploader-input').length == 0) {
$('body').append('<input id="diy-uploader-input" type="file" style="position: absolute; top: -1000px; left: -1000px;" accept="' + accept + '">');
}
// 2. 並綁定點擊事件,用於觸發 實際執行上傳
var $this = this;
$('#diy-uploader-input').on('change', function(event) {
var file = event.target.files[0];
var url = $this.doUpload(file);
// 執行回調
callback(file, url);
});
// 3. 執行模擬點擊
$('#diy-uploader-input').click();
}
封裝上傳功能
現在我們已經擁有了上傳圖片的能力,接下來就是要將這個能力添加到我們的「Markdown 筆記」中,我們需要支持兩個功能:
- 在彈出上傳的窗口中增加一個按鈕,以便我們使用自定義的上傳
- 上傳成功後將相關信息回填到窗口中的對應字段
當時還沒怎麼接觸過 HTML
和 JavaScript
,但這兩個功能比較簡單,編程的基本原理也沒有使用新的知識體系,所以很快就能寫出能完成功能的代碼(省略中間處理各種問題的過程)。
// 初始化,當md文件上傳彈框出來的時候,添加上傳圖片按鈕
function init() {
// 有道雲筆記用 on 綁定 DOMNodeInserted 不生效 - -|||
$('body')[0].addEventListener("DOMNodeInserted", function(e){
// 如果是 markdown 上傳圖片的節點被添加
if(e.target.nodeName.toLowerCase()== 'markdown-upload-image') {
var divButtonBarSelector = 'body > dialog-overlay > div > div > div.widget-dialog-body > markdown-upload-image > div > div.button-bar';
// 如果 底部按鈕欄已出來,並且沒添加過 上傳按鈕,則添加 上傳按鈕
if($(divButtonBarSelector).length == 1 && $('#diy-uploader-button').length == 0) {
// 添加按鈕
var uploaderButton = '<div id="diy-uploader-button" class="loadbtn local-img" style="margin-right:15px;height:34px">上傳圖片</div>';
$('body > dialog-overlay > div > div > div.widget-dialog-body > markdown-upload-image > div > div.button-bar').prepend(uploaderButton);
// 給按鈕添加事件
$('#diy-uploader-button').on('click', function() {
component.uploader.upload('image/*', feature.mdImageUploader.backfillPage);
});
}
}
}, false);
}
// 回填頁面
function backfillPage(file, url) {
// 填入url
var urlSelector = 'body > dialog-overlay > div > div > div.widget-dialog-body > markdown-upload-image > div > div:nth-child(2) > div.edit-container > input';
$(urlSelector).val(url);
// 觸發 input 事件,更新雙向綁定的數據
tool.trigger(urlSelector, 'input');
// 填入文件名
var nameSelector = 'body > dialog-overlay > div > div > div.widget-dialog-body > markdown-upload-image > div > div:nth-child(3) > div.edit-container > input';
var name = file.name.substring(0, file.name.lastIndexOf('.'));
$(nameSelector).val(name);
// 觸發 input 事件,更新雙向綁定的數據
tool.trigger(nameSelector, 'input');
}
至此我們已經實現了有道雲筆記支持「Markdown 筆記」上傳圖片的功能。可以直接將 loader.js 中的代碼拷貝至 Tampermonkey 中即可實現非會員上傳。
小結
這一段腳本是兩年前寫的,但是至今仍舊可以使用。雖然時隔很久,腳本實現的具體細節早已忘記,但是當我看到我這豐富的注釋時,還是可以回想起來當時想法及每段代碼的邏輯。寫注釋也是我一直堅持的好習慣,平時寫業務代碼中沒有這麼詳細的注釋去解釋每一行的操作邏輯,但仍舊會在每一段相對獨立的操作開始時註明其功能等信息。
本文首發於公眾號:滿賦諸機(點擊查看原文) 開源在 GitHub :reading-notes/tampermonkey/note-youdao