canvas 的 getImageData 和 toDataUrl 跨域問題

来源:https://www.cnblogs.com/Jomsou/archive/2020/07/18/13334741.html
-Advertisement-
Play Games

背景是這樣的,母親節的時候,我們有個需求就是用戶可以長按或者點擊一個按鈕進行截圖後去分享我們的活動,然而我們的圖片例如頭像,採用又拍雲做 cdn 優化,所以意味著圖片的鏈接跟主頁面所在功能變數名稱不一樣,當需要需要對 canvas 圖片進行 getImageData() 或 toDataURL() 操作的時 ...


背景是這樣的,母親節的時候,我們有個需求就是用戶可以長按或者點擊一個按鈕進行截圖後去分享我們的活動,然而我們的圖片例如頭像,採用又拍雲做 cdn 優化,所以意味著圖片的鏈接跟主頁面所在功能變數名稱不一樣,當需要需要對 canvas 圖片進行 getImageData()toDataURL() 操作的時候,跨域問題就出來了。

對於跨域的圖片,只要能夠在網頁中正常顯示出來,就可以使用 canvas 的 drawImage() API 繪製出來。但是如果你想更進一步,通過 getImageData() 方法獲取圖片的完整的像素信息,則多半會出錯。

舉例來說,使用下麵代碼獲取 github 上的自己頭像圖片信息:

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

var img = new Image();
img.onload = function () {
    context.drawImage(this, 0, 0);
    context.getImageData(0, 0, this.width, this.height);
};
img.src = 'https://avatars3.githubusercontent.com/u/496048?s=120&v=4';';

結果在 Chrome 瀏覽器下顯示如下錯誤:

Uncaught DOMException: Failed to execute ‘getImageData’ on ‘CanvasRenderingContext2D’: The canvas has been tainted by cross-origin data.

出錯信息截圖

Firefox 瀏覽器錯誤為:

SecurityError: The operation is insecure.

如果使用的是 canvas.toDataURL()方法,則會報:

Failed to execute ‘toDataURL’ on ’HTMLCanvasElement’: Tainted canvased may not be exported

原因其實都是一樣的,跨域導致。

那有沒有什麼辦法可以解決這個問題呢?

可以試試 crossOrigin 屬性。

HTML crossOrigin 屬性解決資源跨域問題

在 HTML5 中,有些元素提供了支持 CORS(Cross-Origin Resource Sharing)(跨域資源共用)的屬性,這些元素包括 ,`` 等,而提供的屬性名就是 crossOrigin 屬性。

因此,上面的跨域問題可以這麼處理:

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

var img = new Image();
img.crossOrigin = '';
img.onload = function () {
    context.drawImage(this, 0, 0);
    context.getImageData(0, 0, this.width, this.height);
};
img.src = 'https://avatars3.githubusercontent.com/u/496048?s=120&v=4';';

增加一個 img.crossOrigin = '' 即可,雖然 JS 代碼這裡設置的是空字元串,實際上起作用的屬性值是 anonymous

crossOrigin 可以有下麵兩個值:

關鍵字 釋義
anonymous 元素的跨域資源請求不需要憑證標誌設置。
use-credentials 元素的跨域資源請求需要憑證標誌設置,意味著該請求需要提供憑證。

其中,只要 crossOrigin 的屬性值不是 use-credentials,全部都會解析為 anonymous,包括空字元串,包括類似 'abc' 這樣的字元。

例如:

img.crossOrigin = 'abc';
console.log(img.crossOrigin);    // 結果是'anonymous'

crossOrigin 解析為 anonymous

另外還有一點需要註意,那就是雖然沒有 crossOrigin 屬性,和設置 crossOrigin="use-credentials" 在預設情況下都會報跨域出錯,但是性質上卻不一樣,兩者有較大區別。

crossOrigin 相容性

IE11+(IE Edge),Safari,Chrome,Firefox 瀏覽器均支持,IE9 和 IE10 會報 SecurityError 安全錯誤,如下截圖:

img

crossOrigin 屬性為什麼可以解決資源跨域問題?

crossOrigin=anonymous 相對於告訴對方伺服器,你不需要帶任何非匿名信息過來。例如 cookie,因此,當前瀏覽器肯定是安全的。

就好比你要去別人家裡拿一件衣服,crossOrigin=anonymous 相對於告訴對方,我只要衣服,其他都不要。如果不說,可能對方在衣服里放個竊聽的工具什麼的,就不安全了,瀏覽器就會阻止。

下載到本地

IE10 瀏覽器不支持 crossOrigin 怎麼辦?

我們請求圖片的時候,不是直接通過 new Image(),而是藉助 ajax 和 URL.createObjectURL() 方法曲線救國。

代碼如下:

var xhr = new XMLHttpRequest();
xhr.onload = function () {
    var url = URL.createObjectURL(this.response);
    var img = new Image();
    img.onload = function () {
        // 此時你就可以使用canvas對img為所欲為了
        // ... code ...
        // 圖片用完後記得釋放記憶體
        URL.revokeObjectURL(url);
    };
    img.src = url;
};
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.send();

此方法不僅 IE10 瀏覽器 OK,原本支持 crossOrigin 的諸位瀏覽器也是支持的。

也就多走一個 ajax 請求,還可以!

根據,根據實踐發現,在 IE 瀏覽器下,如果請求的圖片過大,幾千像素那種,圖片會載入失敗,我猜是超過了 blob 尺寸限制。

後來採用的解決方案是:把圖片下載到本地(前端或者是後端都可以,最後採用我前端來做)

    getAvator(user, func) {
      window.URL = window.URL || window.webkitURL;  // Take care of vendor prefixes.
      var xhr = new XMLHttpRequest();
      xhr.open('GET', user.avatar, true);
      xhr.responseType = 'blob';
      xhr.send()

      xhr.onload = function(e) {
        const {target} = e
        const {status, response, readyState} = target
        if (readyState == 4 && status == 200) {
          var blob = response;
          var img = document.createElement('img');
          img.classList.add("avatar")
          var reader = new window.FileReader();
          reader.readAsDataURL(blob);
          reader.onloadend = function() {
            var base64data = reader.result;
            img.src = base64data;
          };
          func && func(img)
        }
      };
    },

設置 nginx 代理

如 PHP 添加響應頭信息,* 通配符表示允許任意功能變數名稱:

header("Access-Control-Allow-Origin: *");

或者指定功能變數名稱:

header("Access-Control-Allow-Origin: www.zhangxinxu.com");

html2canvas 真實採坑記和建議

  1. 如果使用 vue 做數據渲染,不要在生成頁做太多數據處理的操作,提前把動態數據處理好,否則即便用 $nextTick 也會有在生成圖片時數據不完整的情況
  2. 引用 CDN 上的圖片,需要設置 useCORS 為 true,同時要保證所有圖片載入完成後再生成,可使用 new Imaage 做預載入和判斷是否全部 load
  3. 用背景 background,生成的圖片清晰度不夠,會模糊;用 img 引入的方式可避免這個問題
  4. 在 iOS 系統的 13.4.1,無法生成圖片,需要退回到 1.0.0-rc.4 版本,不要使用 1.0.0-rc.5 版本,issues 地址:https://github.com/niklasvh/html2canvas/issues/2205
  5. 可把生成的圖片設置透明度 opacity 為 0,蓋在原有元素之上,便於在微信保存,不會因為生成的圖和原有元素略微有差距,而抖動。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • #安裝Node環境 在node.js01中我大概瞭解了什麼是node.js,這次進入起步階段,首先要安裝下Node環境。 ###開始安裝 查看當前Node環境的版本號 win+r輸入cmd進入命令行,輸入node --version 我這裡並沒有安裝!!! 下載:https://nodejs.org ...
  • 主要是用到了after偽類和字體符號。 1 input{ 2 -webkit-appearance: none; 3 -moz-appearance: none; 4 appearance: none; 5 display: inline-block; 6 } 7 input:after{ 8 co ...
  • 1.<video></video> 用於定義視頻,如影視片段 語法<video src="XXXmovie.mp4" controls></video> 支持視頻格式:mp4、ogg移動端、webM高清 常用屬性: src,視頻的地址url autoplay,視頻就緒後自動播放 controls,向 ...
  • 五角星形線的笛卡爾坐標方程式可設為: r=10+(3*sin(θ*2.5))^2 x=r*cos(θ) y=r*sin(θ) (0≤θ≤2π) 根據這個曲線方程,在[0,2π]區間取一系列角度值,根據給定角度值計算對應的各點坐標,然後在計算出的坐標位置繪製一個填充色交替變換的小圓,從而得到沿五角星形 ...
  • 1.新增類型 電子郵件類型,語法<input type="email"/>,input中輸入的內容必須包含“@”,並且“@”後面必須有內容 搜索類型,語法<input type="search"/>,輸入搜索關鍵字的文本框 URL類型,語法<input type="url"/>,輸入web站點的文本 ...
  • 一、安裝node.js(https://nodejs.org/en/) 下載完畢後,可以安裝node,建議不要安裝在系統盤(如C:)。 二、設置nodejs prefix(全局)和cache(緩存)路徑 1、在nodejs安裝路徑下,新建node_global和node_cache兩個文件夾 2、設 ...
  • 1.粒子文本的實現原理 粒子文本的實現原理是:使用兩張 canvas,一張是用戶看不到的canvas1,用來繪製文本;另一張是用戶看到的canvas2,用來根據canvas1中繪製的文本數據來生成粒子。 先在canvas1中用如下的語句繪製待顯示的文本。 ctx1.font = '100px Pin ...
  • 定位 定位:通過定位可以將元素擺放在頁面中任意位置 語法:position屬性設置元素的定位 可選值:static:預設值,開啟定位 relative開啟相對定位 absolute開啟絕對定位 fixed開啟固定定位 相對定位:當元素設置position:relative;開啟元素的相對定位 1 開 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...