LoginSignup
0
0

More than 3 years have passed since last update.

Cloud FunctionsでImageMagick(aheckmann/gm)を使う

Last updated at Posted at 2019-06-26
package.json
  "dependencies": {
    ...
    "axios": "^0.19.0",
    "gm": "^1.23.1"
  },    
  "devDependencies": {
    ...
    "@types/gm": "^1.18.3",
  }

import axios from 'axios';
import * as gm from 'gm';
const im = gm.subClass({ imageMagick: true });

export class ImageToSmallPng {

    convert = async (imageUrl: string): Promise<Buffer> => {
        const res = await axios.get(imageUrl, { responseType: 'arraybuffer' });
        const data: ArrayBuffer = res.data;
        const image = im(Buffer.from(data));
        const png = await this.toPngBuffer(image);
        return png;
    }

    toPngBuffer = async (image: gm.State): Promise<Buffer> => {
        return new Promise((resolve, reject) => {
            image.resize(16, 16).toBuffer('PNG', (err, buffer) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(buffer);
                }
            })
        });
    }
}

みたいな。諸事情で結局使わなかったコードなので供養。もしもgmを使わず直にやるなら


import axios from 'axios';
import { exec } from 'child_process';
import * as fs from 'fs';
import * as utils from '../utils';

export class ImageToSmallPng {

    convert = async (imageUrl: string): Promise<Buffer> => {
        const res = await axios.get(imageUrl, { responseType: 'arraybuffer' });
        const data: ArrayBuffer = res.data;

        const tmpSource = '/tmp/' + utils.sha1(iconUrl);
        const tmpDist = `${tmpSource}.png`;
        await this.writeFile(tmpSource, Buffer.from(data));
        await this.toSmallPng(tmpSource, tmpDist);
        return this.readFile(tmpDist);
    }

    toSmallPng = async (tmpSource: string, tmpDist: string): Promise<string> => {
        return new Promise((resolve, reject) => {
            const template = `convert "${tmpSource}" -thumbnail 16x16 -alpha on -background none -flatten "${tmpDist}"`;
            exec(template, {}, (err, result) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(result);
                }
            })
        });
    }

    writeFile = (path: string, data: Buffer): Promise<string> => {
        return new Promise((resolve, reject) => {
            fs.writeFile(path, data, {}, (err) => {
                if (err) {
                    reject(err);
                } else {
                    resolve();
                }
            })
        });
    }

    readFile = (path: string): Promise<Buffer> => {
        return new Promise((resolve, reject) => {
            fs.readFile(path, (err, result) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(result);
                }
            })
        });
    }
}

とかなんとか。なおtmpSource/tmpDistに任意のユーザー入力が混ざったらもちろんやばいし、アプリケーション側でもurl先の画像形式を見てホワイトリストした方が良いのではないかと思う。

ちなみに、ちゃんとは確認がとれてないのだけれどcloud functionsに入っているImageMagickは対応する画像形式が結構絞られていて、少なくともfaviconなどで使われるico形式(Microsoft icon)は2019/6/26現在サポートされていない。たぶん。

See Also

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0