img標籤處理重定向url

需求及分析

項目是web App.需兼容ios, android.html

  1. img佔位圖顯示
  2. url被重定向的圖片前端緩存

知乎有人提類似的問題: https://www.zhihu.com/questio...,可是沒有好的答案.前端

佔位圖天然是爲了體驗.vue

前端緩存圖片,也沒啥稀奇,大部分瀏覽器本身都作好的.android

可是考慮到img url是來自服務端redirect的302響應呢?ios

服務端之因此這麼設計,是考慮到圖片資源可能被其餘cdn分發,總之來自不一樣的存儲,可是考慮兼容這些存儲,須要統一url,就是下面的origin url的形式nginx

origin url:     xxx/files/5:
redirect to local:     /storage/adsaf2qe12.jpg;
or redirect to cdn:     /xx.cdn.com/adsaf2qe12.jpg;

img標籤訪問重定向url的過程

1.初次訪問 /files/22web

GET /files/705%22 HTTP/1.1
Host: xxx:8000                                                        Accept: image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5
Connection: keep-alive
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Faraday Futu      re
Accept-Language: zh-cn                                                         
Accept-Encoding: gzip, deflate

2.返回 302chrome

HTTP/1.1 302 Found                                                               
Server: nginx                                                                    
Content-Type: text/html; charset=UTF-8                                           
Transfer-Encoding: chunked                                                       
Connection: keep-alive                                                           
X-Powered-By: PHP/7.2.19                                                         
Cache-Control: no-cache                                                          
Date: Mon, 12 Aug 2019 23:06:06 GMT                                              
Location: http://xxxx:8000/storage/2019/07/26/0650/eWNojunNteHaADwujoHeBPSucm1Ac1DlTENiroXL.jpeg                 
Expires: Mon, 12 Aug 2019 23:06:05 GMT
X-matching: api_begin                                                        
X-matching: api_end

注意:axios

response body是1個頁面,就是圖片的展現,img的src實現裏應該只關心302和Location的url.

3.訪問真實地址/storage/2019/07/26/0650/xxx.jpegapi

GET /storage/2019/07/26/0650/eWNojunNteHaADwujoHeBPSucm1Ac1DlTENiroXL.jpeg HTTP/1.1
Host: 54.223.41.252:8000
Accept: image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5
Connection: keep-alive
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Faraday Future
Accept-Language: zh-cn
Referer: http://xx/news/79?lang=zh&follow=false
Accept-Encoding: gzip, deflate

4.返回 200

HTTP/1.1 200 OK
Server: nginx
Date: Mon, 12 Aug 2019 23:06:06 GMT
Content-Type: image/jpeg
Content-Length: 331683
Last-Modified: Fri, 26 Jul 2019 06:50:25 GMT
Connection: keep-alive
ETag: "5d3aa2b1-50fa3"
Expires: Wed, 11 Sep 2019 23:06:06 GMT
Cache-Control: max-age=2592000
X-matching: img_begin
X-matching: img_end
Accept-Ranges: bytes

靜態資源由nginx返回,帶了cache控制的header,正常狀況就應該按http緩存的規則來了.

即使是重定位,對chrome這樣的強緩存策略的瀏覽器也沒撒問題.

問題就在於,咱們的項目是web app. 對於ios的WKWebview的img.src引起的訪問,在重定向的前提下是無視緩存的

必須本身來實現前端緩存機制.

佔位圖

首先,切換url來實現佔位圖的效果:

<img v-for="(image,index) in saved_images" :src="image.url"
            :class="layout(saved_images.length)"
            @click="clickImage(index)"
            style="object-fit: cover"
            @load="loadedImage(index)"
    >

vue的核心思路:

watch: {
    //watch images數據,裏面包含url
    'images': {
        handler(newVal) {
            // console.log(this.images);
            let that = this;
            //not video
            if( !this.hasVideo) {
                //newVal is a array, save url;
                newVal.forEach(val => {
                    // 緩存圖片的api,核心在這裏
                    that.apiImg.fetchImg(val.url,response=>{
                         // base64編碼
                        //response來自cache 或者第1次訪問的結果
                        val.url = response;
                    });
                    that.image_urls.push(val.url);
                    //place holder
                    val.url = resources.placeHolder;
                });
            }
            this.saved_images = newVal;
        },
        immediate: true,
        // deep: true,
    }

帶前端緩存的img api

前端緩存須要瞭解下 cookie->localstorage-->indexDB.

我最終選擇了localforage庫,結合axios-cache-adapter來作請求的緩存.

代碼核心功能:

1. 配置localforage,使用INDEXEDDB;
2. 配置axios-cache-adapter
3. 圖片轉儲base64 
4. 調用localforage的setItem/getItem;

最終實現以下:

import {axios} from '../utils/http';
import localforage from 'localforage'
import memoryDriver from 'localforage-memoryStorageDriver'
import {setup} from 'axios-cache-adapter'
import {setupCache} from 'axios-cache-adapter'

// Register the custom `memoryDriver` to `localforage`
localforage.defineDriver(memoryDriver)
// Create `localforage` instance
const forageStore = localforage.createInstance({
  // List of drivers used
  driver: [
    localforage.INDEXEDDB,
    localforage.LOCALSTORAGE,
    memoryDriver._driver
  ],
  size: 10000000,
  // Prefix all storage keys to prevent conflicts
  name: 'img-cache'
});

const cache = setupCache({
  //過時時間
  maxAge: 15 * 60 * 1000,
  store: forageStore
});

const api = axios.create(
  {
    adapter: cache.adapter
  }
);

export default {

  fetchImg: async function (url, fn) {
    const config = {
      responseType: 'arraybuffer'
    };
    //get from store
    await forageStore.getItem(url, (err, value) => {
      if (!err && value) {
        console.log(forageStore);
        console.log('already cached');
        fn(value);
      } else {
        console.log('1st time')
        api.get(url, config,).then(async function (response) {
           //base64 format 
          let bas64Url = "data:" + response.headers['content-type'] + ";base64," + btoa(
            new Uint8Array(response.data).reduce((data, byte) => data + String.fromCharCode(byte), '')
          );
          //store 
          await forageStore.setItem(url, bas64Url, () => {
            console.log('set item ok');
            return fn(url);
          });

        }).catch(function (reason) {
          console.log(reason);
        })
      }
    });
  },
}
相關文章
相關標籤/搜索