JSを使用して画像を効果的に圧縮する方法

JSを使用して画像を効果的に圧縮する方法

序文

同社のモバイル事業では、ユーザーが写真をアップロードする際に、フロントエンドで写真サイズを圧縮してからサーバーにアップロードする必要があります。これにより、モバイル端末のアップストリームトラフィックが削減され、ユーザーのアップロード待ち時間が短縮され、ユーザーエクスペリエンスが最適化されます。

ちなみに、この記事の事例はプラグインとしてまとめられ、npmにアップロードされています。npm install js-image-compressor -D でインストールして使用することができます。github からダウンロードできます。

そこで、この論文では以下の問題を解決しようとします。

  • Image オブジェクト、データ URL、Canvas、ファイル (Blob) 間の変換関係を理解し​​ます。
  • 画像圧縮の主要技術。
  • 非常に大きな画像を圧縮すると黒い画面が表示される問題。

変換関係

実際のアプリケーションで考えられるシナリオ: ほとんどの場合、ユーザーがアップロードした File オブジェクトを直接読み取り、それをキャンバスに読み書きし、Canvas API を使用して圧縮し、圧縮後に File (Blob) オブジェクトに変換して、リモート イメージ サーバーにアップロードします。場合によっては、base64 文字列を圧縮してから、それを base64 文字列に変換してリモート データベースに渡したり、File (Blob) オブジェクトに変換したりする必要があることもあります。一般的に、次のような変換関係があります。

具体的な実装

以下では、変換関係図の変換方法を一つずつ実装していきます。

file2DataUrl(ファイル、コールバック)

ユーザーがページ タグ <input type="file" /> を通じてアップロードしたローカル イメージは、日付 URL 文字列形式に直接変換されます。 FileReader ファイル読み取りコンストラクターを使用できます。 FileReader オブジェクトを使用すると、Web アプリケーションは、File または Blob オブジェクトを使用して読み取るファイルまたはデータを指定し、コンピューターに保存されているファイル (または生データ バッファー) の内容を非同期的に読み取ることができます。インスタンス メソッド readAsDataURL はファイルの内容を読み取り、それを base64 文字列に変換します。読み取り後、ファイルの内容はインスタンス属性の結果で利用できるようになります。

関数 file2DataUrl(ファイル, コールバック) {
 var リーダー = 新しい FileReader();
  reader.onload = 関数(){
    コールバック(reader.result);
  };
  reader.readAsDataURL(ファイル);
}

データ URL は、プレフィックス (data:)、データのタイプを示す MIME タイプ、テキスト以外の場合はオプションの base64 タグ、およびデータ自体の 4 つの部分で構成されます。

データ:<メディアタイプ>,<データ>

たとえば、png 形式の画像は base64 文字列に変換されます: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAgAElEQVR4XuxdB5g。

file2Image(ファイル、コールバック)

ユーザーがアップロードした写真をローカルにキャッシュし、img タグで表示したい場合は、上記の方法で変換した base64 文字列を写真の src として使用するほか、URL オブジェクトを直接使用して、File や Blob に保存されているデータの URL を参照することもできます。オブジェクト URL を使用する利点は、ファイルの内容を直接 JavaScript に読み込まなくても使用できることです。これを行うには、ファイル コンテンツが必要な場所にオブジェクト URL を指定するだけです。

関数file2Image(ファイル、コールバック) {
 var image = 新しい Image();
 var URL = window.webkitURL || window.URL;
 (URL) {
 var url = URL.createObjectURL(ファイル);
    イメージ.onload = 関数() {
      コールバック(画像);
      URL.revokeObjectURL(url);
    };
    イメージのURLをコピーします。
  } それ以外 {
    inputFile2DataUrl(ファイル、関数(dataUrl) {
      イメージ.onload = 関数() {
        コールバック(画像);
      }
      画像のサイズは、
    });
  }
}

注: オブジェクト URL を作成するには、window.URL.createObjectURL() メソッドを使用し、File または Blob オブジェクトを渡します。データが不要になった場合は、そのデータが占めているコンテンツを解放するのが最善です。ただし、オブジェクト URL を参照するコードがある限り、メモリは解放されません。メモリを手動で解放するには、オブジェクトの URL を URL.revokeObjectURL() に渡します。

url2Image(url, コールバック)

画像リンク(URL)を通じてImageオブジェクトを取得します。画像の読み込みは非同期なので、取得したImageオブジェクトを返すためにコールバック関数 callback 内に配置します。

関数 url2Image(url, コールバック) {
 var image = 新しい Image();
  イメージのURLをコピーします。
  イメージ.onload = 関数() {
    コールバック(画像);
  }
}

image2Canvas(画像)

drawlmage() メソッドを使用して、Canvas オブジェクトに Image オブジェクトを描画します。

drawImage には 3 つの構文形式があります。

void ctx.drawImage(画像、dx、dy);

void ctx.drawImage(画像、dx、dy、dWidth、dHeight);

void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

パラメータ:

コンテキストに描画される画像要素。

sx は、Image に基づいて選択ボックスの左上隅の X 軸座標を描画します。

sy イメージに基づいて選択ボックスの左上隅の Y 軸座標を描画します。

sWidth は選択ボックスの幅を描画します。

sHeight は選択ボックスの幅を描画します。

dx ターゲット キャンバス上のイメージの左上隅の X 軸座標。

ターゲット キャンバス上の dy イメージの左上隅の Y 軸座標。

dWidth ターゲットキャンバスに描画されるイメージの幅。

dHeight ターゲット キャンバスに描画されるイメージの高さ。

関数 image2Canvas(画像) {
 var キャンバス = document.createElement('キャンバス');
 var ctx = canvas.getContext('2d');
  キャンバスの幅 = image.naturalWidth;
  キャンバスの高さ = image.naturalHeight;
  ctx.drawImage(画像、0、0、キャンバス.幅、キャンバス.高さ);
 キャンバスを返します。
}

canvas2DataUrl(キャンバス、品質、タイプ)

HTMLCanvasElement オブジェクトには、表示する画像を含むデータ URL を返す toDataURL(type, encodingOptions) メソッドがあります。出力形式と品質も指定できます。

パラメータは次のとおりです。

type: 画像形式。デフォルトは image/png です。

画像フォーマットが image/jpeg または image/webp として指定されている場合、encoderOptions では 0 から 1 の範囲で画像品質を選択できます。値が範囲外の場合、デフォルト値 0.92 が使用され、他のパラメータは無視されます。

関数 canvas2DataUrl(キャンバス、品質、タイプ) {
 canvas.toDataURL(type || 'image/jpeg', quality || 0.8) を返します。
}

dataUrl2Image(データUrl、コールバック)

画像リンクは base64 文字列にすることもでき、これは Image オブジェクト src に直接割り当てることができます。

関数 dataUrl2Image(dataUrl, コールバック) {
 var image = 新しい Image();
  イメージ.onload = 関数() {
    コールバック(画像);
  };
  画像のサイズは、
}

dataUrl2Blob(データUrl、タイプ)

データ URL 文字列を Blob オブジェクトに変換します。主なアイデアは、まずデータ URL のデータ部分を抽出し、atob を使用して base64 でエンコードされた文字列をデコードし、次にそれを Unicode エンコードに変換し、それを Uint8Array (8 ビットの符号なし整数配列、各要素は 1 バイト) タイプの配列に格納し、最後にそれを Blob オブジェクトに変換することです。

関数 dataUrl2Blob(dataUrl, type) {
 var data = dataUrl.split(',')[1];
 var mimePattern = /^data:(.*?)(;base64)?,/;
 var mime = dataUrl.match(mimePattern)[1];
 var binStr = atob(データ);
 var arr = 新しい Uint8Array(len);

 (var i = 0; i < len; i++) の場合 {
    arr[i] = binStr.charCodeAt(i);
  }
 新しいBlob([arr], {type: type || mime})を返します。
}

canvas2Blob(キャンバス、コールバック、品質、タイプ)

HTMLCanvasElement には、キャンバスに画像を表示するための Blob オブジェクトを作成する toBlob(callback, [type], [encoderOptions]) メソッドがあります。この画像ファイルは、ユーザー エージェントの判断により、キャッシュしたり、ローカルに保存したりできます。 2 番目のパラメータは画像形式を指定します。指定しない場合、画像タイプはデフォルトで image/png になり、解像度は 96dpi になります。 3 番目のパラメータは、image/jpeg 形式の画像の出力画像の品質を設定するために使用されます。

関数 canvas2Blob(キャンバス、コールバック、品質、タイプ){
  canvas.toBlob(function(blob) {
    コールバック(blob);
  }, タイプ || 'image/jpeg'、品質 || 0.8);
}

古いブラウザとの互換性を保つために、toBlob のポリフィル ソリューションとして、データ URL を使用して、HTMLCanvasElement プロトタイプ メソッドとして Blob メソッド dataUrl2Blob を生成できます。

HTMLCanvasElement.prototype.toBlobの場合{
 Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
  値: 関数 (コールバック、タイプ、品質) {
 dataUrl = this.toDataURL(type, quality); とします。
    コールバック(dataUrl2Blob(dataUrl));
  }
 });
}

blob2DataUrl(blob、コールバック)

Blob オブジェクトをデータ URL データに変換します。FileReader のインスタンスの readAsDataURL メソッドは、ファイルの読み取りだけでなく、Blob オブジェクト データの読み取りもサポートしているため、上記の file2DataUrl メソッドをここで再利用できます。

関数 blob2DataUrl(blob, コールバック) {
  file2DataUrl(blob、コールバック);
}

blob2Image(blob、コールバック)

Blob オブジェクトを Image オブジェクトに変換します。URL オブジェクトを通じてファイルを参照できます。また、Blob などのファイルのようなオブジェクトを参照することもできます。同様に、上記の file2Image メソッドを再利用できます。

関数blob2Image(blob, コールバック) {
  file2Image(blob、コールバック);
}

アップロード(URL、ファイル、コールバック)

画像 (圧縮済み) をアップロードするには、FormData を使用してファイル オブジェクトを渡し、XHR 経由でファイルを直接サーバーにアップロードします。

関数アップロード(URL、ファイル、コールバック) {
 var xhr = 新しい XMLHttpRequest();
 var fd = 新しいFormData();
  fd.append('ファイル', ファイル);
  xhr.onreadystatechange = 関数 () {
 xhr.readyState === 4 && xhr.status === 200 の場合 {
 //アップロード成功コールバック && callback(xhr.responseText);
    } それ以外 {
 新しいエラーをスローします(xhr);
    }
  }
  xhr.open('POST', url, true);
  xhr.send(fd);
}

FileReaderを使用してファイルの内容を読み取り、アップロード用にバイナリに変換することもできます。

関数アップロード(url, ファイル) {
 var リーダー = 新しい FileReader();
 var xhr = 新しい XMLHttpRequest();

  xhr.open('POST', url, true);
  xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');

  リーダー.onload = 関数() {
    xhr.send(リーダーの結果);
  };
  reader.readAsBinaryString(ファイル);
}

シンプルな画像圧縮の実装

上記のさまざまな画像変換方法の具体的な実装を理解した後、それらを共通オブジェクト util にカプセル化し、圧縮変換フローチャートと組み合わせると、ここでは画像圧縮を簡単に実装できます。
まず、アップロードされた画像は Image オブジェクトに変換され、次に Canvas キャンバスに書き込まれ、最後に Canvas オブジェクト API によって画像のサイズと出力が調整され、圧縮が実現されます。

/**
 * シンプルな画像圧縮方法* @param {Object} オプション関連のパラメータ*/
(関数 (win) {
 var REGEXP_IMAGE_TYPE = /^image\//;
 var util = {};
 var デフォルトオプション = {
    ファイル: null、
    品質: 0.8
  };
 var isFunc = function (fn) { return typeof fn === 'function'; };
 var isImageType = function (値) { return REGEXP_IMAGE_TYPE.test(値); };

 /**
   * シンプルな画像圧縮コンストラクター * @param {Object} オプション関連のパラメーター */
 関数SimpleImageCompressor(オプション) {
    オプション = Object.assign({}, defaultOptions, オプション);
 this.options = オプション;
 this.file = オプション.file;
 これを初期化します。
  }

 var _proto = SimpleImageCompressor.prototype;
  win.SimpleImageCompressor = SimpleImageCompressor;

 /**
   * 初期化 */
  _proto.init = 関数init() {
 var _this = これ;
 var ファイル = this.file;
 var オプション = this.options;

 if (!file || !isImageType(file.type)) {
      console.error('画像ファイルをアップロードしてください!');
 戻る;
    }

 if (!isImageType(options.mimeType)) {
      オプション.mimeType = ファイル.type;
    }

    util.file2Image(ファイル、関数(画像) {
 var キャンバス = util.image2Canvas(img);
      ファイルの幅 = img.naturalWidth;
      ファイルの高さ = img.naturalHeight;
      _this.beforeCompress(ファイル、キャンバス);

      util.canvas2Blob(キャンバス、関数(blob) {
        キャンバスの幅をピクセル単位で指定します。
        キャンバスの高さを blob に設定します。
        オプション.成功 && オプション.成功(blob);
      }、options.quality、options.mimeType)
    })
  }

 /**
   * 圧縮前、画像フック関数の読み取り後 */
  _proto.beforeCompress = 関数beforeCompress() {
 if (isFunc(this.options.beforeCompress)) {
 this.options.beforeCompress(this.file);
    }
  }

 // `util` パブリック メソッドの定義を省略 // ...

 // インスタンスの静的プロパティに `util` パブリックメソッドを追加します for (key in util) {
 (util.hasOwnProperty(キー))の場合{
      SimpleImageCompressor[キー] = util[キー];
    }
  }
})(ウィンドウ)

この単純な画像圧縮メソッドの呼び出しと入力パラメータ:

var fileEle = document.getElementById('file');

fileEle.addEventListener('change', 関数() {
  ファイル = this.files[0];

 var オプション = {
    ファイル: ファイル、
    品質: 0.6、
    mimeタイプ: 'image/jpeg',
 // 圧縮前のコールバック beforeCompress: function (result) {
      console.log('圧縮前の画像サイズ: ', result.size);
      console.log('MIMEタイプ: ', result.type);
 // アップロードした画像をページ上でプレビューします // SimpleImageCompressor.file2DataUrl(result, function (url) {
 // document.getElementById('origin').src = url;
 // })
    },
 // 圧縮成功コールバック success: function (result) {
      console.log('圧縮後の画像サイズ: ', result.size);
      console.log('MIMEタイプ: ', result.type);
      console.log('圧縮率: ', (result.size / file.size * 100).toFixed(2) + '%');

 // 圧縮された画像を生成し、ページに表示します // SimpleImageCompressor.file2DataUrl(result, function (url) {
 // document.getElementById('output').src = url;
 // })

 // リモート サーバーにアップロード // SimpleImageCompressor.upload('/upload.png', result);
    }
  };

 新しい SimpleImageCompressor(オプション);
}、 間違い);

このデモが単純すぎることに抵抗がなければ、ここをクリックして試してみることができます。複数の種類の写真をアップロードするのに十分な忍耐力がある場合、次の問題がまだ残っていることがわかります。

圧縮された出力画像のサイズは元の画像サイズに固定されますが、実際には、サイズを圧縮するという目的を達成しながら出力画像のサイズを制御する必要がある場合があります。

png形式の画像はそのまま圧縮されるため、圧縮率が高くなく、「減るどころか増える」という現象が起きる場合があります。

場合によっては、他の形式を PNG 形式に変換すると、「減少ではなく増加」現象が発生することもあります。

大きなサイズの PNG 形式の画像は、一部の携帯電話では圧縮後に黒い画面に表示される場合があります。

画像圧縮の改善

「ローマは一日にして成らず」ということわざにもあるように、上記の実験を通して、多くの欠点が見つかりました。以下では、問題を一つずつ分析し、解決策を探っていきます。

圧縮された出力画像のサイズは元の画像サイズに固定されますが、実際には、サイズを圧縮するという目的を達成しながら出力画像のサイズを制御する必要がある場合があります。

圧縮された画像の変形を避けるために、一般的には幾何学的スケーリングが使用されます。まず、元の画像のアスペクト比を計算する必要があります。ユーザーが設定した高さにアスペクト比を掛けて、幾何学的スケーリング後の幅を取得します。ユーザーが設定した幅よりも小さい場合は、ユーザーが設定した高さがスケーリングの基準として使用されます。それ以外の場合は、幅がスケーリングの基準として使用されます。

var アスペクト比 = 自然な幅 / 自然な高さ;
var width = Math.max(options.width, 0) || naturalWidth;
var height = Math.max(options.height, 0) || naturalHeight;
高さ * アスペクト比 > 幅の場合 {
  高さ = 幅 / アスペクト比;
} それ以外 {
  幅 = 高さ * アスペクト比;
}

出力画像のサイズが決まりました。次のステップでは、このサイズに合わせてキャンバスを作成し、その上に画像を描画します。ここで、上記の image2Canvas メソッドを少し変更することができます。

関数 image2Canvas(image, destWidth, destHeight) {
 var キャンバス = document.createElement('キャンバス');
 var ctx = canvas.getContext('2d');
  キャンバスの幅 = destWidth || image.naturalWidth;
  キャンバスの高さ = destHeight || image.naturalHeight;
  ctx.drawImage(画像、0、0、キャンバス.幅、キャンバス.高さ);
 キャンバスを返します。
}

png形式の画像はそのまま圧縮されるため、圧縮率が高くなく、「減るどころか増える」という現象が起きることがある

一般的に、png 形式の画像を独自の形式に圧縮することは推奨されません。圧縮率が理想的ではなく、場合によっては画像の品質が悪化する可能性があるためです。

「具体的な実装」には 2 つの圧縮キー API があるためです。

toBlob(callback, [type], [encoderOptions]) encodingOptions パラメータは、image/jpeg 形式の画像の出力画像の品質を設定するために使用されます。

toDataURL(type, encodingOptions パラメータencoderOptions 画像フォーマットをimage/jpegまたはimage/webpに指定した場合、画質を0~1の範囲で選択できます。

どちらも png 形式の画像に圧縮効果はありません。

妥協案があります。しきい値を設定できます。png 画像の品質がこの値より低い場合は、png 形式で圧縮して出力します。これにより、最悪の出力結果がそれほど悪くなりません。これに基づいて、圧縮された画像のサイズが「減少するのではなく増加する」場合は、出力ソース画像をユーザーに処理します。画像の品質が一定値を超える場合は、jpeg 形式に圧縮します。

// `png` 形式の画像サイズが `convertSize` を超える場合は、`jpeg` 形式に変換します。if (file.size > options.convertSize && options.mimeType === 'image/png') {
  オプション.mimeType = 'image/jpeg';
}
// 一部のコードを省略 // ...
// ユーザーが期待する出力の幅と高さがソース画像の幅と高さより大きくない場合、出力ファイルのサイズはソースファイルより大きくなり、ソースファイルが返されます if (result.size > file.size && !(options.width > naturalWidth || options.height > naturalHeight)) {
  結果 = ファイル;
}

大きなサイズの PNG 形式の画像は、一部の携帯電話では圧縮後に黒い画面に表示される場合があります。

主要なブラウザはそれぞれ異なる最大キャンバスサイズをサポートしているため

画像サイズが大きすぎる場合、同じサイズのキャンバスを作成してその上に画像を描画すると、生成されたキャンバスに画像ピクセルがなく、キャンバス自体のデフォルトの背景色が黒になるという異常な状況が発生し、画像が「黒い画面」のように表示されます。

ここでは、出力画像の最大幅と高さを制御することで、生成されたキャンバスが範囲外になるのを防ぎ、デフォルトの黒い背景を透明色で覆うことで「黒い画面」の問題を解決できます。

// ...
// 最小および最大の幅と高さを制限します var maxWidth = Math.max(options.maxWidth, 0) || Infinity;
var maxHeight = Math.max(options.maxHeight, 0) || 無限大;
var minWidth = Math.max(options.minWidth, 0) || 0;
var minHeight = Math.max(options.minHeight, 0) || 0;

最大幅 < 無限大 && 最大高さ < 無限大) {
 (最大高さ * アスペクト比 > 最大幅)の場合 {
    最大高さ = 最大幅 / アスペクト比;
  } それ以外 {
    最大幅 = 最大高さ * アスペクト比;
  }
} そうでなければ(最大幅<無限大){
  最大高さ = 最大幅 / アスペクト比;
} そうでなければ(最大高さ<無限大){
  最大幅 = 最大高さ * アスペクト比;
}

最小幅 > 0 && 最小高さ > 0 の場合 {
 最小高さ * アスペクト比 > 最小幅の場合 {
    minHeight = minWidth / アスペクト比;
  } それ以外 {
    最小幅 = 最小高さ * アスペクト比;
  }
} それ以外の場合 (minWidth > 0) {
  minHeight = minWidth / アスペクト比;
} そうでなければ (minHeight > 0) {
  最小幅 = 最小高さ * アスペクト比;
}

幅 = Math.floor(Math.min(Math.max(幅, 最小幅), 最大幅));
高さ = Math.floor(Math.min(Math.max(高さ, minHeight), maxHeight));

// ...
// デフォルトの塗りつぶし色 (#000) を上書きします
var fillStyle = '透明';
コンテキストの塗りつぶしスタイル = 塗りつぶしスタイル;

この時点で、上記の予想外の問題は一つずつ解決されました

要約する

ページタグ <input type="file" /> を介したローカル画像のアップロードから画像圧縮までのプロセス全体を整理し、実際の使用時にまだ存在するいくつかの予期しない状況もカバーし、対応するソリューションを提供しました。画像圧縮の改良版をプラグインとしてまとめ、npm にアップロードしました。npm install js-image-compressor -D でインストールして使用できます。github からダウンロードできます。

上記は、JS を使用して画像を効果的に圧縮する方法の詳細です。JS による画像の効果的な圧縮の詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • jsは画像の純粋なフロントエンド圧縮を実現します
  • 画像を圧縮する js の例 (画像サイズは変更せず、容量のみを削減)
  • js 経由で圧縮画像アップロード機能を実装する
  • JSはアップロードされた画像のbase64の長さを圧縮する機能を実現します
  • js を使用して layui にアップロードされた画像を圧縮する
  • JavaScript を使用して画像を圧縮および暗号化する方法をご存知ですか?

<<:  MySQLのSQLモードの特徴のまとめ

>>:  Zookeeper&Kafka クラスターを構築するための Docker の実装

推薦する

Linux でスペースを含むファイルを削除する (ディレクトリではない)

日常業務では、スペースのないファイルに遭遇することがよくあります。これにより、削除操作がはるかに簡単...

Dockerで複数のSpringbootを実行するための詳細なチュートリアル

Dockerは複数のSpringbootを実行する1番目: ポートマッピング 2番目: メモリサイズ...

MySQL innodb B+ツリーの高さを取得する方法

序文MySQL の InnoDB エンジンがインデックスの保存に B+tree を使用する理由は、デ...

JavaScript を使用してテーブル情報を追加および削除する

JavaScript 入門JavaScript は軽量なインタープリタ型の Web 開発言語です。言...

Centos7 で yum を使用して Ceph 分散ストレージをインストールするチュートリアル

目次序文yumソース、epelソースを設定するCephソースの設定Cephとそのコンポーネントをイン...

DockerのIDEA構成プロセス

IDEA は Java で最も一般的に使用されている開発ツールであり、Docker は最も人気のある...

ノードをMySQLデータベースに接続する際に発生する問題と解決策

今日、MySQL の新しいバージョン (8.0.21) をインストールしましたが、ノード フレームワ...

ユーザー中心設計

最近、デジタル デザイン コミュニティで「誰が何を担当するのか」という明らかな混乱についてよく質問さ...

Vueのsync修飾子の詳細な説明

目次1. 手順2. 修飾語3. .sync 修飾子4. まとめ1. 手順指示とは命令です。文字通りの...

Vue ファースト スクリーン パフォーマンス最適化コンポーネントの知識ポイントの概要

Vue ファースト スクリーン パフォーマンス最適化コンポーネントVue ファースト スクリーン パ...

例を通してMySQLの更新がテーブルをロックするかどうかを判定する

2つのケース: 1. 索引あり 2. 索引なし前提条件:方法: コマンドラインを使用してシミュレート...

クールなバーコードエフェクトの作り方を教えます

声明:この記事では、Web ページ制作技術を使用して問題を包括的に解決するという考え方を反映して、W...

Linux システムで .sh ファイルを実行する方法

Linux システムで .sh ファイルを実行する方法は 2 つあります。たとえば、ルート ディレク...

Nginx Rewrite の使用シナリオとコード例の詳細な説明

Nginx Rewriteの使用シナリオ1. URL アドレスジャンプ。たとえば、ユーザーが pm....

JavaScript の知識: コンストラクタも関数である

目次1. コンストラクタの定義と呼び出し2. 新しいキーワードの目的3. コンストラクタの問題: メ...