是這樣的需求,有一個web頁面,裏面圖片的上傳和預覽來自於一個獨立的文件服務器,對http的請求須要進行訪問權限的設置,就是在請求的header里加一個Authorization的字段。上傳好說我用的Axios直接添加一個header就好了,可是預覽就比較麻煩了,由於img
這個標籤圖片下載展現是瀏覽器本身實現的,沒有辦法去修改。因此首先想到就是經過接口添加自定義header轉發請求或者其餘經過接口的方案了,那怎麼經過前端頁面去實現這個功能,首先聲明的是這裏用了一些新的API,因此若是是一些比較老的瀏覽器那就無法這麼作了。html
問題分析:img
標籤的src屬性只能設置url,不能設置此次請求的header。既然這樣,能不能經過別的方式先把圖片下載下來而後再給img標籤做展現,至關於把src屬性的下載和展現分紅了兩步,先調用接口獲取到了數據,而後再把數據給展現出來,也就是src裏的值不是一個url地址而是一個數據流。前端
能夠這樣,首先經過Object.defineProperty定義一個authSrc屬性用來替換src屬性的值,而後在window.onload裏等dom加載完之後去再下載圖片。node
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Proxy Image</title> <script> Object.defineProperty(Image.prototype, 'authsrc', { writable : true, enumerable : true, configurable : true }) window.onload = () => { let img = document.getElementById('img'); let url = img.getAttribute('authsrc'); let request = new XMLHttpRequest(); request.responseType = 'blob'; request.open('get', url, true); request.setRequestHeader('Authorization', '憑證信息'); request.onreadystatechange = e => { if (request.readyState == XMLHttpRequest.DONE && request.status == 200) { img.src = URL.createObjectURL(request.response); img.onload = () => { URL.revokeObjectURL(img.src); } } }; request.send(null); } </script> </head> <body> <img width="100" height="100" id="img" authsrc="http://39.106.118.122/images/image_201909111450326.jpg"> </body> </html>
這樣雖然能夠實現功能,可是每次還須要執行額外的腳本,不能在Dom加載完的時候自動去下載展現,不夠優雅。能不能自動去下載展現呢ios
自定義元素不太瞭解的能夠參考這裏Using custom elements,這裏還有個w3c的草案autonomous-custom-element。git
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Proxy Image</title> <script> let requestImage = function (url, element) { let request = new XMLHttpRequest(); request.responseType = 'blob'; request.open('get', url, true); request.setRequestHeader('Authorization', '憑證信息'); request.onreadystatechange = e => { if (request.readyState == XMLHttpRequest.DONE && request.status == 200) { element.src = URL.createObjectURL(request.response); element.onload = () => { URL.revokeObjectURL(element.src); } } }; request.send(null); } class AuthImg extends HTMLImageElement { constructor() { super(); this._lastUrl = ''; } static get observedAttributes() { return ['authSrc']; } connectedCallback() { let url = this.getAttribute('authSrc'); if (url !== this._lastUrl) { this._lastUrl = url; requestImage(url, this); } console.log('connectedCallback() is called.'); } } window.customElements.define('auth-img', AuthImg, {extends: 'img'}); </script> </head> <body> <img width="100" height="100" is="auth-img" authSrc="http://39.106.118.122/images/image_201909111450326.jpg"> </body> </html>
這裏我是在Electron客戶端用的,是經過進程間通訊的方式獲取到了用戶憑證信息,若是是部署在服務器上的話,應該使用其餘方式。github
let app = http.createServer((request, response) => { let config = { host: 'xxx.com', method: 'GET', path: request.url, headers: { Authorization: '用戶憑證' } }; let proxyRequest = http.request(config, proxyResponse => { proxyResponse.on('data', data => { response.write(data, 'image/jpg'); }); proxyResponse.on('end', () => { response.end(); }); response.writeHead(proxyResponse.statusCode, proxyResponse.headers); }) request.on('data', data => { proxyRequest.write(data, 'image/jpg'); }) request.on('end', () => { proxyRequest.end(); }) }); app.listen(port, () => { console.log('has start proxy server!'); })
既然做請求轉發,那Nginx天然也是能夠的,可是Nginx裏添加header都是固定,無法去修改,想到了一個方式,先請求一個地址攜帶token,而後自定義一個變量,去設置這個值。這個方式有點噁心,一來是把token暴露了出來,二來是容易形成誤傷,一不當心就把token更新了,並且假如Nginx重啓了這時候token也沒了。只做爲一個思路拓展了,是不能這麼搞的,大概像下面這樣。web
server { ... set $AUTH_TOKEN ""; location /token/([0-9a-z])$ { set $AUTH_TOKEN $1; return 200; } location /image { proxy_pass http://xxx.com; proxy_set_header Authorization $AUTH_TOKEN; } }