圖片跨域規律探尋


先說結論:

  1. canvas.toDataURL API中用到的圖片,必須添加crossOrigin屬性設置,否則會報被污染的canvas無法被導出的錯誤

  2. url相同,crossOrigin屬性的圖,在頁面中通過html img標簽和js-dom Image對象不管加載多少次,瀏覽器只請求服務器一次。從緩存中讀取時,多次加載也只讀取一次。

  3. 頁面加載多幅url相同的圖片,如果這些圖片中有些設置了跨域屬性,有的未跨域屬性,只要設置了跨域屬性的圖之后會加載沒有跨域屬性的圖,那么最后緩存的就是沒有跨域屬性的圖。

  4. 如果緩存的是沒有跨域屬性的圖片,設置了跨域屬性的html img標簽,js-dom Image對象從緩存中加載圖片,會報跨域錯誤。如果緩存的是設置了跨域屬性的圖片,html img標簽,js-dom Image對象 無論是否設置跨域屬性,都可以從緩存中正常加載圖片。

再看實驗過程:

1.分別加載沒有設置crossOrigin屬性的html-img和js-img圖片,調用canvas.toDataURL轉換data URI,執行時都會報錯-被污染的canvas無法被導出,這個錯誤是由於canvas使用了未設置跨域的圖片資源引起的,只有設置了crossOrigin屬性的圖片資源,才能被canvas復用。

1.1 加載沒有設置crossOrigin="anonymous"屬性的html-img圖片,執行canvas.toDataURL

<img  alt=""  id="html-img"
  src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>
<div  id="js-canvas-box"></div>
<script>
    // 將圖片繪制在canvas畫布上
    function convertCanvasToImage(image) {
      const canvas = document.createElement('canvas');
      canvas.width = image.width;
      canvas.height = image.height;
      canvas.getContext('2d').drawImage(image, 0, 0);
      // 從canvas畫布導出圖片
      const img = new Image();
      img.src = canvas.toDataURL('image/png');
      return img;
    }
    
    // 通過html-img標簽加載圖片
    const htmlImg = document.querySelector('#html-img');
    htmlImg.onload = function () {
      const img=convertCanvasToImage(this);
      document.querySelector('#js-canvas-box').appendChild(img);
    };
    </script>

報如下錯誤:

1.2 加載沒有設置crossOrigin="anonymous"屬性的js-image圖片,執行canvas.toDataURL

<div id="js-canvas-box" />
<script>
    // 將圖片繪制在canvas畫布上
    function convertCanvasToImage(image) {
      const canvas = document.createElement('canvas');
      canvas.width = image.width;
      canvas.height = image.height;
      canvas.getContext('2d').drawImage(image, 0, 0);
      // 從canvas畫布導出圖片
      const img = new Image();
      img.src = canvas.toDataURL('image/png');
      return img;
    }
    
    // 通過js-dom加載圖片
    const jsImg = new Image();
    jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png';
    jsImg.onload = function () {
      const img=convertCanvasToImage(this);
      document.querySelector('#js-canvas-box').appendChild(img);
    };
    </script>

報如下錯誤:

2.分別加載設置crossOrigin屬性時html-img和js-img圖片,調用canvas.toDataURL執行都正常。

crossOrigin可以有下面兩個值:

anonymous 元素的跨域資源請求不需要憑證標志設置。
use-credentials 元素的跨域資源請求需要憑證標志設置,意味着該請求需要提供憑證

只要crossOrigin的屬性值不是use-credentials,全部都會解析為anonymous,包括空字符串。

2.1 加載設置crossOrigin屬性時html-img圖片,執行canvas.toDataURL,結果正確。

<img  alt=""  id="html-img" crossOrigin=""
  src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>
<div id="js-canvas-box" />
<script>
    // 將圖片繪制在canvas畫布上
    function convertCanvasToImage(image) {
      const canvas = document.createElement('canvas');
      canvas.width = image.width;
      canvas.height = image.height;
      canvas.getContext('2d').drawImage(image, 0, 0);
      // 從canvas畫布導出圖片
      const img = new Image();
      img.src = canvas.toDataURL('image/png');
      return img;
    }
    
    // 通過html-img標簽加載圖片
    const htmlImg = document.querySelector('#html-img');
    htmlImg.onload = function () {
      const img=convertCanvasToImage(this);
      document.querySelector('#js-canvas-box').appendChild(img);
    };
    </script>

2.2 加載設置crossOrigin屬性時js-img圖片,執行canvas.toDataURL,結果正確。

<div id="js-canvas-box" />
<script>
    
    function convertCanvasToImage(image) {
      // 將圖片繪制在canvas畫布上
      const canvas = document.createElement('canvas');
      canvas.width = image.width;
      canvas.height = image.height;
      canvas.getContext('2d').drawImage(image, 0, 0);

      // 從canvas畫布導出圖片
      const img = new Image();
      img.src = canvas.toDataURL('image/jpg');
      return img
    }
    
    // 通過js-dom加載圖片
    const jsImg = new Image();
    // 只要crossOrigin的屬性值不是use-credentials,全部都會解析為anonymous,包括空字符串。
    jsImg.crossOrigin=""
    jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png';
    jsImg.onload = function () {
      const image=convertCanvasToImage(jsImg);
      document.querySelector('#js-canvas-box').appendChild(image);
    };
</script>

3.再看看從緩存加載,會不會報錯。啟用緩存,分別加載設置crossOrigin屬性時html-img和js-img圖片,執行canvas.toDataURL,也都沒有報錯。

3.1 啟用緩存,加載設置crossOrigin屬性時html-img圖片,執行canvas.toDataURL,結果正確。

<img  alt=""  id="html-img"
 crossOrigin="anonymous"
 src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>
<div id="js-canvas-box" />
<script>
    
    function convertCanvasToImage(image) {
      // 將圖片繪制在canvas畫布上
      const canvas = document.createElement('canvas');
      canvas.width = image.width;
      canvas.height = image.height;
      canvas.getContext('2d').drawImage(image, 0, 0);

      // 從canvas畫布導出圖片
      const img = new Image();
      img.src = canvas.toDataURL('image/jpg');
      return img
    }
    // 通過html-img加載圖片
    const htmlImg = document.querySelector('#html-img');
    htmlImg.onload = function () {
      const image=convertCanvasToImage(this);
      document.querySelector('#js-canvas-box').appendChild(image);
    };
</script>

3.2 啟用緩存,加載設置crossOrigin屬性時js-img圖片,執行canvas.toDataURL,結果正確。

<div id="js-canvas-box" />
<script>
    
    function convertCanvasToImage(image) {
      // 將圖片繪制在canvas畫布上
      const canvas = document.createElement('canvas');
      canvas.width = image.width;
      canvas.height = image.height;
      canvas.getContext('2d').drawImage(image, 0, 0);

      // 從canvas畫布導出圖片
      const img = new Image();
      img.src = canvas.toDataURL('image/jpg');
      return img
    }
    
 // 通過js-dom加載圖片
    const jsImg = new Image();
    // 只要crossOrigin的屬性值不是use-credentials,全部都會解析為anonymous,包括空字符串。
    jsImg.crossOrigin=""
    jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png';
    jsImg.onload = function () {
      const image=convertCanvasToImage(this);
      document.querySelector('#js-canvas-box').appendChild(image);
    };
</script>

 

4. 通過html-img和js-img加載url相同,crossOrigin屬性的圖,只加載一次。從緩存中讀取時,也只讀取一次

4.1 html-img和js-img都未設置crossOrigin,加載同一幅圖,只加載一次。

<img
  alt="img"
  id="html-img"
  src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>
<div id="js-canvas-box"></div>
<script>
  const jsImg = new Image();
  jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png';
  jsImg.onload = function () {
    document.querySelector('#js-canvas-box').appendChild(jsImg);
  };
</script>

從緩存中讀取,只讀取了一次。

4.2 html-img和js-img都設置crossOrigin,加載同一幅圖,只加載一次。

<img
  alt="img"
  id="html-img"
  crossOrigin=""
  src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>
<div id="js-canvas-box"></div>
<script>
  const jsImg = new Image();
  jsImg.crossOrigin=""
  jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png';
  jsImg.onload = function () {
    document.querySelector('#js-canvas-box').appendChild(this);
  };
</script>

從緩存中讀取,只讀取一次。

5. 再看看通過html-img的方式加載url相同,crossOrigin屬性不同的情景,頁面加載多個url一樣的html-img圖片,如果在跨域屬性圖片之后加載了沒有跨域屬性的圖片,那么最后緩存的是未設置crossOrigin屬性的圖片,刷新頁面,那些設置了crossOrigin屬性的圖片,從緩存中加載圖片時,會報跨域錯誤

這是因為:

  • 在頁面加載的過程中,圖片會被瀏覽器緩存,如果再次遇到url和crossOrigin屬性相同的圖片,直接會從緩存中讀取,如果url相同,crossOrigin屬性與之前緩存的圖片不同,瀏覽器會重新請求,並重新緩存,覆蓋之前緩存的同一張圖。可是緩存中的圖片跨域屬性一旦從跨域變成不跨域,之后瀏覽器便不會在覆蓋之前的緩存。緩存的圖片始終保持為不跨域。
  • 緩存的圖片如果是未設置跨域屬性的圖片,html-img標簽設置了crossOrigin屬性,從緩存加載,會觸發跨域問題。緩存的圖片如果是設置了跨域屬性的圖片,無論html-img標簽是否設置crossOrigin屬性,從緩存加載,都不會觸發跨域問題。

5.1 最后緩存的是沒有設置crossOrigin屬性的圖片, 從緩存中加載時,觸發了html img標簽中設置了crossOrigin屬性圖片的跨域。

<!-- 緩存的是沒有跨域屬性的圖片 -->
<img
alt="img-3-no"
src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>

<!-- 緩存被覆蓋,緩存的是有跨域屬性的圖片 -->
<img
alt="img-2-anonymous"
crossOrigin="anonymous"
src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>


<!-- 緩存再次被覆蓋,緩存的是沒有跨域屬性的圖片 -->
<img
alt="img-3-no"
src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>
<!-- 緩存中同一幅圖的跨域屬性一旦由跨域變成不跨域,之后瀏覽器不會再修改圖片的跨域屬性 -->
<img
alt="img-4-anonymous"
crossOrigin="anonymous"

src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>

1.5kb的圖片是沒有跨域屬性的圖片,1.6kb的圖片是設置了跨域屬性的圖片,從網絡請求面板可以看到,最后請求的是沒有跨域屬性的圖片,意味着最后緩存的也是沒有跨域屬性的圖片。

設置了跨域屬性的html img標簽,從緩存中加載沒有跨域屬性的圖片,瀏覽器會報跨域錯誤。

5.2 最后緩存的圖是設置了crossOrigin的圖片,從緩存中加載時,  不會觸發html img標簽中未設置crossOrigin屬性圖片的跨域。

<!-- 緩存的是沒有跨域屬性的圖片 -->
<img
alt="img-3-no"
src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>

<!-- 緩存過,不再緩存 -->
<img
alt="img-2-no"
src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>


<!-- 緩存過,不再緩存 -->
<img
alt="img-3-no"
src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>
<!-- 緩存被覆蓋,緩存的是有跨域屬性的圖片 -->
<img
alt="img-4-anonymous"
crossOrigin="anonymous"

src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>

 從網絡請求面板可以看出,最后請求的是1.6kb的設置了跨域屬性的圖片,意味着緩存中最后保持的也是有跨域屬性的圖片

 html img標簽即使未設置跨域屬性,也能利用緩存中保存的設置了跨域屬性的圖片,不會報錯。

6.再看看html-img和js-img混搭加載的情景, 頁面加載多個url相同的html-img和js-img混搭圖片,前面的結論依舊成立。

6.1 最后緩存的是沒有設置crossOrigin屬性的圖片,從緩存加載時,設置了crossOrigin屬性的圖片會報跨域錯誤。

<img
  alt="img"
  id="html-img"
  crossOrigin="anonymous"
  src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>
<div id="js-canvas-box"></div>
<script>
  const jsImg = new Image();
  jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png';
  jsImg.onload = function () {
    document.querySelector('#js-canvas-box').appendChild(jsImg);
  };
</script>

 

 

6.2 最后緩存的是設置了crossOrigin屬性的圖片,從緩存加載時,不會觸發沒有設置crossOrigin屬性的圖片跨域錯誤。

<img
  alt="img"
  id="html-img"
  src="https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png"
/>
<div id="js-canvas-box"></div>
<script>
  const jsImg = new Image();
  jsImg.crossOrigin="anonymous"
  jsImg.src ='https://sf3-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/app-install.81ece51.png';
  jsImg.onload = function () {
    document.querySelector('#js-canvas-box').appendChild(jsImg);
  };
</script>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM