WeChat公衆番号の画像をアリクラウドOSSにアップロードします.


最近はWeChat公式アカウントを作っていますが、画像をアリクラウドOSSにアップロードする必要があります.この機能をする過程で、いろいろな回り道をして、いろいろな方法を試しました.今はこれらの方法と考え方を記録して、忘れないようにします.
一、ブラウザで直接OSSに伝える
この方法が一番簡単です.WeChat公衆番号のジャンプページはQQブラウザに基づいているので、直接HTMLのinput要素を使って画像を選択することができます.
OSSにはPost Objectというインターフェースがあります.ファイル以外にもOSSに保存するパス、ポリシー、自分のOSSアプリケーションのaccessKeyId、署名などの他のフィールドがあります.
フォームの作成が必要です.一般的に2つの方法があります.
1)DOMノードを作成し、フォームをアップロードします.
以下のコードのようにform要素を構成し、$('form').submit()を利用して提出します.

2)Html 5のFormDataオブジェクトを使ってアップロードする
以下のようにFormDataオブジェクトを構築し、ajaxまたはfetch postフォームデータを通過します.
const formData = new FormData();
formData.append('key', filePath); // OSS     
formData.append('policy', policy); //   
formData.append('OSSAccessKeyId', accessKeyId); // OSS     
formData.append('success_action_status', '200'); //      
formData.append('signature', signature); //   
formData.append('file', file); //     ,$('input[name="pic"]').files[0]
二、サービス端末でWeChatの画像をダウンロードしてからOSSに保存する.
上記の方法は簡単で直接ですが、写真はアルバムからしか選択できません.写真を撮ってアップロードするには、微信JS-SDKを通じてカメラを呼び出す必要があります.
wx.chooseImage({
  count: 1, //   9
  sizeType: ['original', 'compressed'], //             ,      
  sourceType: ['album', 'camera'], //              ,      
  success: function (res) {
    //          ID  ,localId    img   src      
    var localIds = res.localIds; 
  }
});
ここで問題がありますが、WeChat JS-SDKは画像を選択して戻ってくるのは、実際の画像ファイルではなく、実際の画像ファイルなので、フォームを作成してOSSをアップロードすることができません.
どうすればいいですか?構想:画像を先にWeChatのサーバー(最大3日間保存)にアップロードして、微信のダウンロードマルチメディアファイルインターフェース(http://file.api.weixin.qq.com...)画像をサーバーにダウンロードし、OSSにアップロードします.
クライアントコード:
wx.chooseImage({
  count: 1, //   9
  sizeType: ['original', 'compressed'],
  sourceType: ['album', 'camera'],
  success: function (res) {
    var localIds = res.localIds;
    wx.uploadImage({
      localId: localIds[0], //           ID, chooseImage    
      isShowProgressTips: 1, //    1,      
      success: function (res) {
        var serverId = res.serverId; //          ID
        // do something ...
        //            api,  serverId,         OSS     
        doSomething();
      }
    });
  }
});
小tips:画像を選ぶ時はcompressedを選択すると、WeChatが自動的に画像を圧縮してくれます.公式文書もアップロードされたマルチメディアファイルがフォーマットとサイズを制御すると説明しています.画像はjpg形式と1 M以下のサイズに制御されます.ですから、写真の大きさはあまり考えなくてもいいです.実測では、8 Mの画像を圧縮した後は120 KBぐらいです.
サービスのコードは3回の進化を経て改善されました.
1)fsを利用して写真をローカルに書く
const fs = require('fs');
const request = require('require');
const OSS = require('ali-oss').Wrapper;

const ossClient = new OSS({
  accessKeyId: 'your access key',
  accessKeySecret: 'your access secret',
  bucket: 'your bucket name',
  region: 'oss-cn-hangzhou'
});

//       accessToken,     
const accessToken = 'access token';
const mediaId = 'xxxxxxx'; //        id
const destPath = `weixin/images/201702/${mediaId}.jpg`; // OSS    ,        
const wxReq = request(`http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=${accessToken }&media_id=${mediaId}`);

//     pipe     
wxReq.pipe(fs.createWriteStream(`${mediaId}.jpg`));
wxReq.on('end', () => {
  co(function* () {
    const result = yield ossClient.putStream(destPath, fs.createReadStream(`${mediaId}.jpg`), {timeout: 30 * 60 * 1000});
    console.log('         ', result);
    fs.unlink(`${mediaId}.jpg`);
    // res.status(200).json(result);
  }).catch(err => {
    console.warn(err);
    //res.status(500).send('      ');
  });
});
このような方式は頻繁にファイルを書く必要があります.
2)memory-streamsモジュールを利用して画像をメモリに書き込みます.
const request = require('require');
const OSS = require('ali-oss').Wrapper;
const streams = require('memory-streams');

const ossClient = new OSS({
  accessKeyId: 'your access key',
  accessKeySecret: 'your access secret',
  bucket: 'your bucket name',
  region: 'oss-cn-hangzhou'
});

const accessToken = 'access token';
const mediaId = 'xxxxxxx'; //        id
const destPath = `weixin/images/201702/${mediaId}.jpg`; // OSS    
const writer = new streams.WritableStream();
const wxReq = request(`http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=${accessToken }&media_id=${mediaId}`);

wxReq.pipe(writer);
wxReq.on('end', () => {
  co(function* () {
    const result = yield ossClient.put(destPath, writer.toBuffer(), {timeout: 30 * 60 * 1000});
    console.log('         ', result);
    // res.status(200).json(result);
  }).catch(err => {
    console.warn(err);
    //res.status(500).send('      ');
  });
});
このように写真をメモリに保存します.もし合併量が大きいなら、メモリが爆発してもいいですか?感じはやはり取るべきでないです.
3)ダウンロードした画像のストリームを直接OSSファイルに書き込みます.
長い間苦労しましたが、こんなに簡単で優雅なことができると分かりました.
const request = require('require');
const OSS = require('ali-oss').Wrapper;

const ossClient = new OSS({
  accessKeyId: 'your access key',
  accessKeySecret: 'your access secret',
  bucket: 'your bucket name',
  region: 'oss-cn-hangzhou'
});

const accessToken = 'access token';
const mediaId = 'xxxxxxx'; //        id
const destPath = `weixin/images/201702/${mediaId}.jpg`; // OSS    
const wxReq = request(`http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=${accessToken }&media_id=${mediaId}`);

wxReq.on('response', (response) => {
  // request     response         ossClient
  co(function* () {
    const result = yield ossClient.putStream(destPath, response, {timeout: 30 * 60 * 1000});
    console.log('         ', result);
    // res.status(200).json(result);
  }).catch(err => {
    console.warn(err);
    //res.status(500).send('      ');
  });
});
この方法は前の二つの方法の中間ステップを省き、もっと簡潔で直接的で、個人的には一番いいと思います.