Posted at

エクセル内画像を抜き出して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年間無償の開発者ライセンスを使ってみてください!