Help us understand the problem. What is going on with this article?

エクセル内画像を抜き出してkintoneに登録

More than 3 years have passed since last update.

kintoneって全文検索という便利な機能があるんですよ。
エクセル業務のkintone乗り換えも多いんですよ。

乗り換え時に、全文検索もあるし、とりあえず手元にあるエクセルを全部登録しとくか!
という話もあると思うんですよ・・・多分・・・きっとどこかに・・・

ということで(?)、昔のデータを探す時にエクセルをダウンロードすることなく、kintone上でエクセル内の画像だけでも見られたら便利だなと思い立ち・・・思い立ったが吉日!
まずはdeveloper networkなどで似た記事がないか確認 φ(・ω・ )
なかったのでエクセルファイル内の画像をkintoneに登録するサンプルプログラムを作ってみました。

できること

kintoneに添付されたエクセル内の画像を添付ファイルとして登録する。
文字データは未対応ですが、もう少し頑張ればできると思います。

出来上がりのイメージ

今回は結果から!
↓のように2シートに計5枚の画像が挿入されているエクセルで試してみると・・・
excel5.jpg

上手く行きました!(画面が灰色になっているのはリロードのため)
https://gyazo.com/ca147339e435c40b59bab856dd2829ba

エクセル5ファイル、計40個の画像でも正常に動作することを確認しています。

前提

  • エクセルの形式はxlsx
  • 画像の種類はJPG、GIF、PNG
  • kintoneのフォーム設定は以下の通り
フィールド名 フィールドタイプ フィールドコード
エクセルファイル 添付ファイル excelFile
画像ファイル 添付ファイル imageFile
スペース button
sample.js
(function() {
    'use strict';

    kintone.events.on('app.record.detail.show', parseExcel);

    function parseExcel(event) {
        var button = kintone.app.record.getSpaceElement('button');
        $(button).append('<button id="parseExcelButton">エクセルファイル内の画像をkintoneに登録!</button>');
        $(button).append('<div id="parseExcelMessage"></div>');

        var record = event.record;

        $('#parseExcelButton').on('click', function(e) {
            $(this).prop('disabled', true);
            e.preventDefault();

            var files = record.excelFile.value;
            var excelFileKeys = [];
            for (var i = 0; i < files.length; i++) {
                if (files[i].contentType !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
                    continue;
                }
                excelFileKeys.push(files[i].fileKey);
            }
            if (excelFileKeys.length === 0) {
                $('#parseExcelMessage').text('エクセルファイルが登録されていません。');
                return false;
            }

            Promise.all(excelFileKeys.map(function(excelFileKey) {
                $('#parseExcelMessage').text('エクセルファイル取得中...');

                return getFile(excelFileKey);
            })).then(function(blobList) {
                console.log('[SUCCESS] get excel files.');

                $('#parseExcelMessage').text('エクセルファイル内の画像取得中...');

                // 2次元配列を1次元配列に
                var blobs = Array.prototype.concat.apply([], blobList);

                return Promise.all(blobs.map(function(blob) {
                    return pickupImages(blob);
                }));
            }).then(function(imageList) {
                console.log('[SUCCESS] get image files.');

                $('#parseExcelMessage').text('画像登録中...');

                var images = Array.prototype.concat.apply([], imageList);
                return Promise.all(images.map(function(image) {
                    return uploadImage(image);
                }));
            }).then(function(fileKeyList) {
                console.log('[SUCCESS] upload image files.');

                $('#parseExcelMessage').text('レコード更新中...');

                var fileKeys = Array.prototype.concat.apply([], fileKeyList);
                return updateRecord(fileKeys);
            }).then(function() {
                location.reload();
            }).catch(function(error) {
                console.log('[ERROR]', error);
            });
        });
    }

    // kintoneに添付されたファイルをダウンロード
    function getFile(fileKey) {
        var url = '/k/v1/file.json?fileKey=' + fileKey;
        var df = new $.Deferred();
        var xhr = new XMLHttpRequest();

        xhr.open('GET', url, true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.responseType = 'blob';

        xhr.onload = function(e) {
            if (this.status === 200) {
                df.resolve(this.response);
            }
        };

        xhr.send();
        return df.promise();
    }

    // エクセルファイル内の画像を取得
    function pickupImages(blob) {
        var df = new $.Deferred();
        var reader = new FileReader();
        reader.readAsDataURL(blob);

        // .xlsxファイルの読み込み
        reader.onload = function(e) {
            var xlsxBase64 = e.target.result.split(',')[1];

            var zip = new JSZip();
            zip = zip.load(xlsxBase64, {base64: true});
            var mediaFiles = zip.folder('xl/media').file(/^image/);

            var images = [];
            for (var i = 0; i < mediaFiles.length; i++) {
                var filePath = mediaFiles[i].name;
                var fileName = filePath.replace(/.*\//, '');
                var fileMime = toMimeType(filePath);

                var arrayBuffer = zip.file(filePath).asArrayBuffer();
                var imageBlob = new Blob([arrayBuffer], {type: fileMime});
                images[i] = {'name': fileName, 'blob': imageBlob};
            }

            df.resolve(images);
        };

        return df.promise();
    }

    // 画像をkintoneに登録し、fileKeyを返す
    function uploadImage(image) {
        return new Promise(function(resolve, reject) {
            var formData = new FormData();
            formData.append('__REQUEST_TOKEN__', kintone.getRequestToken());
            formData.append('file', image.blob, image.name);

            var xhr = new XMLHttpRequest();
            xhr.open('POST', '/k/v1/file.json', true);
            xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
            xhr.onload = function(e) {
                if (this.status === 200) {
                    var res = JSON.parse(this.responseText);
                    resolve(res.fileKey);
                } else {
                    reject(JSON.parse(this.responseText));
                }
            };

            xhr.send(formData);
        });
    }

    // レコード更新
    function updateRecord(fileKeys) {
        var appId = kintone.app.getId();
        var recId = kintone.app.record.getId();

        var putData = {
            'app': appId,
            'id': recId,
            'record': {
                'imageFile': {
                    'value': []
                }
            }
        };

        for (var i = 0; i < fileKeys.length; i++) {
            putData.record['imageFile'].value.push({'fileKey': fileKeys[i]});
        }

        return kintone.api('/k/v1/record', 'PUT', putData);
    }

    // MimeTypeを返す
    function toMimeType(filePath) {
        var reg = /(.*)(?:\.([^.]+$))/;
        var fileExt = filePath.match(reg)[2];

        var mimeType = '';
        switch (fileExt.toLowerCase()) {
            case 'jpg':
                mimeType = 'image/jpeg';
                break;
            case 'jpeg':
                mimeType = 'image/jpeg';
                break;
            case 'png':
                mimeType = 'image/png';
                break;
            case 'gif':
                mimeType = 'image/gif';
                break;
        }

        return mimeType;
    }

})();
sample.css
button#parseExcelButton{
    display: block;
    padding: 10px;
    margin: 20px;
    text-align: center;
    border: 3px solid;
    border-color: #ccc #999 #999 #ccc;
    color: #666
}

button#parseExcelButton:hover{
    background: #aaa;
    color: #fff;
}

div#parseExcelMessage{
    display: block;
    padding: 10px;
    margin: 20px;
    text-align: center;
}

kintone開発者ライセンス

kintone面白そうだな~と思った方は5ユーザー、1年間無償の開発者ライセンスを使ってみてください!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away