0

前端小技巧 - 在網頁 Ctrl-V 貼圖片上傳檔案

 7 months ago
source link: https://blog.darkthread.net/blog/paste-to-upload/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前端小技巧 - 在網頁 Ctrl-V 貼圖片上傳檔案

2024-02-15 11:04 PM 1 1,448

在很多網站看過一種方便設計 - 從別處選取圖片複製到剪貼簿,在網頁空白處按 Ctrl-V,便會將圖片上傳到網站。

總想著,改天我要為自己的網站也加上這個酷功能,就今天吧!

在 IE 退役及Edge 改用 Chromium 引擎後,瀏覽器回到大一統的和平局面(令人想起當年 IE 市佔 95% 的時光),現在寫桌機用網頁輕鬆許多,很少需要煩惱跨瀏覽器問題。加上 HTML5 標準成熟與普及,要從剪貼簿拿到圖片內容遠比想像簡單,用內建 API 短短幾行便能搞定。(最重要的是現在還多了 Github Copilot 魔法)

<!DOCTYPE html>
<html>
<head>
    <title>從剪貼簿貼上圖片</title>
</head>
<body>
    <p>按 Ctrl-V 貼上複製的圖片</p>
    <div id="panel"></div>
    <script>
        document.addEventListener('paste', function (e) {
            const items = e.clipboardData && e.clipboardData.items;
            if (!items) return;
            for (let i = 0; i < items.length; i++) {
                if (items[i].type.indexOf('image') !== -1) {
                    const reader = new FileReader();
                    reader.onload = function (e) {
                        var image = new Image();
                        image.src = e.target.result;
                        document.getElementById('panel').appendChild(image);
                    };
                    reader.readAsDataURL(items[i].getAsFile());
                }
            }
        });
    </script>
</body>
</html>

程式原理很簡單:傾聽 paste 事件,逐一檢查 e.clipboardData.items 的資料選項有沒有圖檔類( Content Type 為 "image/*"),若有就用 FileReader.readAsDataURL() 讀入 DataTransferItem.getAsFile(),轉為 Data URI 格式在網頁插入圖片元素,輕鬆秒殺。

Fig1_638436065118158748.gif

下一步是將其轉為上傳檔案。

基於安全 <input type="file"> 挑選檔案向來限定人工操作,禁止 JavaScript 染指,因此過去我都是採行繞道作法,例如:用 <input type="hidden"> 傳送 Data URI 編碼、改用 AJAX 方式上傳... 等。不過,隨者 HTML 標準成熟普及,現在多了 File API 可用,允許 JavaScript 透過 <input type="file"> .files 屬性 (FileList 型別)讀取或設定上傳內容。有個眉角是 FileList 內含的檔案項目不可修改,需透過 DataTransfer 物件建立整個 FileList 指定給 <input type="file">.files,檔名則由依當下日期時間指定動態產生。

寫了一個簡單 ASPX 驗證:

<%@ Page Language="C#" %>
    <script runat="server">
    protected void Page_Load(object sender, EventArgs e)
        {
            if (Request.Files.Count > 0) {
            HttpPostedFile file = Request.Files[0];
                var fileData = new byte[file.ContentLength];
                file.InputStream.Read(fileData, 0, file.ContentLength);
                Response.ContentType = "text/html";
                Response.Write("<body>");
                Response.Write("<p>" + file.FileName + " (" + file.ContentLength + " bytes) uploaded</p>");
                Response.Write("<img src='data:" + file.ContentType + ";base64," + Convert.ToBase64String(fileData) + "' />");
                Response.Write("</body>");
                Response.End();
            }
        }
    </script>
    <!DOCTYPE html>
    <html>

    <head>
        <title>貼圖檔上傳</title>
        <style>
            html, body { height: 100%; padding: 0; margin: 0; }
            .container {
                display: flex; flex-direction: column; 
                height: 100%; padding: 12px; box-sizing: border-box;
            }
            form { height: 40px; }
            iframe {
                flex-grow: 1; width: 100%;
            }
        </style>
    </head>

    <body>
        <div class="container">
            <form method="post" target="result" enctype="multipart/form-data">
                <input type="file" name="file" id="uploadFiles" />
                <input type="submit" value="上傳" />
            </form>
            <iframe name="result"></iframe>
        </div>
        <script>
            document.addEventListener('paste', function (e) {
                const items = e.clipboardData.items;
                for (let i = 0; i < items.length; i++) {
                    if (items[i].type.indexOf('image') !== -1) {
                        let file = items[i].getAsFile();
                        const fileExt = file.name.split('.').pop();
                        file = new File([file],
                            `Img${new Date().toISOString().substring(0, 19).replace(/[-T:]/g, '')}.${fileExt}`,
                            { type: file.type });
                        const dataTransfer = new DataTransfer();
                        dataTransfer.items.add(file);
                        document.getElementById('uploadFiles').files = dataTransfer.files;
                        break;
                    }
                }
            });
        </script>

    </html>

就醬,從今天起,我寫的網站也能貼圖片上傳功能囉~ (灑花)

Fig2_638436065122356198.gif

註:用 Edge/Chrome/Firefox 測過都沒問題,以上做法應可安心用在供桌機瀏覽的企業內部網站。

補充一個小技巧:若想保留 jpg/gif 圖檔格式,不想一律轉成 png,則不要用軟體或瀏覽器開啟圖檔再複製影像,請選取檔案本體複製、貼上,這樣可上傳原格式檔案,示範如下:

Fig3_638436086968440933.gif


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK