これが要件です。独立したファイル サーバーから画像のアップロードとプレビューを行う Web ページがあります。http のリクエストでは、アクセス権を設定する必要があります。これは、リクエストのヘッダーに Authorization フィールドを追加することです。アップロードの場合は、私が使用しているAxiosに直接ヘッダーを追加するだけで済みますが、プレビューの場合はさらに面倒です。img
ラベル画像のダウンロードと表示はブラウザ自体で実装されており、変更する方法がないためです。したがって、最初に考えられるのは、インターフェイスを介してカスタム ヘッダー転送リクエストを追加するか、インターフェイスを介して他のソリューションを追加することです。次に、フロントエンド ページを通じてこの機能を実現する方法を考えます。最初のステートメントは、ここではいくつかの新しい API が使用されているということです。古いブラウジングです。デバイスではそれができません。
問題分析:img
タグの src 属性は URL のみを設定でき、このリクエストのヘッダーは設定できません。この場合、最初に画像をダウンロードしてから、他の方法で img タグに表示できますか。これは、src 属性のダウンロードと表示を 2 つのステップに分割し、最初にインターフェイスを呼び出してデータを取得し、次にデータを表示します。つまり、src の値は URL アドレスではなく、データ ストリームです。
これは次のようになります。最初に Object.defineProperty を通じて authSrc 属性を定義して src 属性の値を置き換え、次に dom が window.onload にロードされた後にピクチャをダウンロードします。
<!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://threex.top/images/image_201909111450326.jpg">
</body>
</html>
このようにして機能は実現できますが、追加スクリプトを毎回実行する必要があり、Domロード時に自動的にダウンロードして表示することができず、エレガントとは言えません。自動的にダウンロードして表示することはできますか?
カスタム要素経由でロードする
カスタム要素についてあまり詳しくない場合は、「ここでカスタム要素の使用」を参照してください。また、w3c ドラフトのAutonomous-custom-elementもあります。
<!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://threex.top/images/image_201909111450326.jpg">
</body>
</html>
リクエストの転送にノードを使用する
ここでは Electron クライアント上で使用しており、ユーザーの認証情報はプロセス間通信で取得していますが、サーバー上にデプロイする場合は他の方法を使用する必要があります。
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も可能ですが、Nginxで追加されるヘッダーは固定で変更できないため、まずトークンを運ぶアドレスをリクエストし、その値を設定する変数をカスタマイズする方法を考えました。この方法は少し気持ち悪いもので、第一にトークンが公開されること、第二に不慮の怪我を引き起こしやすく、トークンが誤って更新され、Nginx を再起動するとトークンが消えてしまいます。アイデアとして膨らませただけで、こんなことはできない、おおよそ次のような感じです。
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;
}
}