内容
javascript、今回はkintoneを使用し、画像をあるサーバからアップする方法を記載する。
以下のパターンを検証する。
1.外部urlより取得
2.kintone内の画像より取得
外部urlより取得
主な関数、API
・あるURLから画像を取得したい場合fetchを使用する。
・kintoneでファイルをアップロードする。
https://sample.cybozu.com/k/v1/file.json
kintoneでは以下のようになる。
プロセスのアクションボタンを押下したときに発動させる例。
kintone.events.on('app.record.detail.process.proceed', async function(event) {
if (event.action.value === '画像アップロード') { // アクションボタンの名前に合わせて変更
try {
const response = await fetch('外部サーバの画像URL');
const blob = await response.blob();
// kintoneのAPIを使ってblobをアップロード
let formData = new FormData();
formData.append('file', blob, 'filename.jpg'); // filenameは実際のファイル名に変更
const headers = {
'X-Cybozu-API-Token': 'YOUR_API_TOKEN' // ここに実際のAPIトークンを設定してください
};
//画像をアップロード
const uploadResp = await fetch("/k/v1/file.json", {
method: "POST",
headers: headers,
body: formData,
});
const uploadData = await uploadResp.json();
if (uploadData.fileKey) {
record[attachmentFieldCode].value.push({ fileKey: uploadData.fileKey });
}
} catch (error) {
console.error('Error:', error);
}
}
return event;
});
画像の取得元のサーバがCORS(Cross-Origin Resource Sharing)をサポートしている必要がある。CORSの設定がされていないと、ブラウザのセキュリティポリシーにより画像の取得に失敗する可能性あり。
kintoneのAPIの呼び出しに関するレートリミットや、ファイルのサイズ制限なども考慮する必要あり。
上記はasync awaitを使用しているが下記はfetchとthenを使ったPromiseペースを使用した例。
kintone.events.on('app.record.detail.process.proceed', function(event) {
if (event.action.value === '画像アップロード') {
fetch('外部サーバの画像URL')
.then(response => response.blob())
.then(blob => {
var formData = new FormData();
formData.append('file', blob, 'filename.jpg');
return kintone.api(kintone.api.url('/k/v1/file', true), 'POST', {
fileKey: formData
});
})
.then(response => {
if (response.fileKey) {
event.record['添付ファイルフィールドのAPIコード'].value = [response.fileKey];
}
})
.catch(error => {
console.error('Error:', error);
});
}
return event;
});
あと上記においてエラーハンドリングは強化しておくべき。
kintone.events.on('app.record.detail.process.proceed', function(event) {
if (event.action.value === '画像アップロード') {
fetch('外部サーバの画像URL')
.then(response => {
if (!response.ok) {
throw new Error('外部サーバからの画像の取得に失敗しました。');
}
return response.blob();
})
.then(blob => {
var formData = new FormData();
formData.append('file', blob, 'filename.jpg');
return kintone.api(kintone.api.url('/k/v1/file', true), 'POST', {
fileKey: formData
});
})
.then(response => {
if (!response.fileKey) {
throw new Error('kintoneへの画像のアップロードに失敗しました。');
}
event.record['添付ファイルフィールドのAPIコード'].value = [response.fileKey];
})
.catch(error => {
console.error('Error:', error);
alert(error.message); // ユーザへのエラーメッセージの表示
// ここで、エラーログの保存やリトライの機構を実装することも考えられます。
});
}
return event;
});
kitone内の画像で対応
外部urlより取得の場合、CORS問題で取得できない場合がある。
その場合は無理に外部urlで行うよりも、kintone内のアプリより画像を取得してセットとした方が早い。
以下の流れで対応できる。
既存レコードに対してカスタマイズで画像のアップロードが可能
1.JavaScriptカスタマイズでボタン追加
2.カスタマイズボタンクリック処理
3.kintoneのあるアプリ(ファイル管理)より画像を取得
4.取得した画像のアップロード
5.ファイルキーを取得し該当レコードを更新
-
JavaScriptカスタマイズでボタン追加
- kintoneのアプリカスタマイズ機能を使用して、アプリの詳細画面にカスタムボタンを追加します。
- 例:
kintone.app.record.getHeaderMenuSpaceElement().appendChild(button);
-
カスタマイズボタンクリック処理
- ボタンにクリックイベントを追加し、処理を定義します。
- 例:
button.onclick = function() { // 以下の処理を実行 };
-
kintoneのあるアプリ(ファイル管理)より画像を取得
- kintone REST APIを使用して、特定のアプリから画像ファイルを取得します。
- 例:
kintone.api(kintone.api.url('/k/v1/record', true), 'GET', {app: <ファイル管理アプリID>, id: <レコードID>}, function(resp) { // 画像データの取得 });
-
取得した画像のアップロード
- 取得した画像ファイルを、アップロード用のAPIを使用してkintoneにアップロードします。
- 例:
kintone.api(kintone.api.url('/k/v1/file', true), 'POST', {fileKey: <取得した画像のファイルキー>}, function(resp) { // アップロード完了後の処理 });
-
ファイルキーを取得し該当レコードを更新
- アップロードが完了したら、ファイルキーを取得します。
- 取得したファイルキーを使用して、対象のレコードを更新します。
- 例:
var body = {app: <アプリID>, id: <レコードID>, record: {<画像フィールド名>: {value: [<ファイルキー>]}}}; kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', body, function(resp) { // レコード更新完了後の処理 });
これらのステップを組み合わせることで、kintoneのアプリにおいて既存レコードに対して画像のアップロードを実現できます。APIの詳細やパラメータは、kintoneのドキュメントを参照して適宜調整してください。
以下は間違っています!、間違い箇所は保存ボタン押下では、画像をセットできません!
1.画像の取得
2.取得した画像のアップロード
3.アップロードした結果として得られる新しいfileKeyをレコードの添付ファイルフィールドにセット
4.その変更をeventオブジェクトに反映
5.return event;でkintoneの標準の新規登録・更新の動作を続行
async function getAndUploadAttachment(fileKey) {
const headers = {
'X-Requested-With': 'XMLHttpRequest',
};
try {
// 画像を取得
const resp = await fetch(`/k/v1/file.json?fileKey=${fileKey}`, {
method: 'GET',
headers,
});
const blob = await resp.blob();
// 新しい添付ファイルとして登録
const formData = new FormData();
formData.append("file", blob);
const uploadResp = await fetch("/k/v1/file.json", {
method: "POST",
headers,
body: formData,
});
const uploadData = await uploadResp.json();
return uploadData.fileKey;
} catch (error) {
console.error("Error:", error);
throw error;
}
}
//新規、編集時の保存ボタン押下
kintone.events.on('app.record.create.submit,app.record.edit.submit', async function(event) {
const record = event.record;
const attachmentFieldCode = 'YOUR_ATTACHMENT_FIELD_CODE';
record[attachmentFieldCode].value[0].fileKey = await getAndUploadAttachment(fileKey);
if (event.type === 'app.record.create.submit') {
// 新規作成時のみの処理
} else if (event.type === 'app.record.edit.submit') {
// 編集時のみの処理
}
return event;
});
メモ
もう少し具体的に記載
個別関数のエラーハンドリング
async function getImageFileKey(imageAppId, imageRecordId) {
try {
const imageResp = await kintone.api(kintone.api.url('/k/v1/record', true), 'GET', {
app: imageAppId,
id: imageRecordId
});
return imageResp.record['添付ファイルフィールドコード'].value[0].fileKey;
} catch (err) {
console.error('画像のファイルキー取得でエラーが発生しました:', err);
throw new Error('画像のファイルキー取得に失敗しました。');
}
}
async function downloadImage(fileKey) {
try {
const fileResp = await fetch(`/k/v1/file.json?fileKey=${fileKey}`, {
method: 'GET',
headers: { 'X-Requested-With': 'XMLHttpRequest' }
});
if (!fileResp.ok) {
throw new Error('ファイルのダウンロードに失敗しました。');
}
return await fileResp.blob();
} catch (err) {
console.error('画像のダウンロードでエラーが発生しました:', err);
throw err; // 再スローして呼び出し元でキャッチする
}
}
async function updateRecordWithImage(currentAppId, currentRecordId, blob) {
try {
const formData = new FormData();
formData.append('__REQUEST_TOKEN__', kintone.getRequestToken());
formData.append('file', blob, 'image.jpg'); // ファイル名は適宜変更可能
const uploadResp = await kintone.api(kintone.api.url('/k/v1/file', true), 'POST', formData);
const updateResp = await kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', {
app: currentAppId,
id: currentRecordId,
record: {
'更新対象の添付ファイルフィールドコード': {
value: [{ fileKey: uploadResp.fileKey }]
}
}
});
return updateResp;
} catch (err) {
console.error('レコードの更新でエラーが発生しました:', err);
throw new Error('レコードの更新に失敗しました。');
}
}
メイン関数のエラーハンドリング
async function updateImageProcess() {
try {
// 各ステップの関数を呼び出し
const fileKey = await getImageFileKey(画像アプリID, 画像レコードID);
const blob = await downloadImage(fileKey);
const updateResp = await updateRecordWithImage(kintone.app.getId(), kintone.app.record.getId(), blob);
console.log('レコード更新完了', updateResp);
} catch (err) {
// 呼び出し元でキャッチされたエラーの処理
console.error('画像更新プロセスでエラーが発生しました:', err);
alert(err.message);
}
}
はい、おっしゃる通りです。個別の関数でエラーが発生した場合、throw new Error
を使用してエラーをスローし、それをメイン関数のtry...catch
ブロックでキャッチするというのが一般的なアプローチです。また、catch
ブロック内でthrow
を使用してエラーを再スロー(rethrow)することもできます。これにより、エラーの伝播と適切なハンドリングが可能になります。
エラーの再スロー(Rethrow)
エラーの再スローは、エラーをキャッチして何らかの処理を行った後に、そのエラーを上位のコードに伝播させるために使われます。以下はその一例です:
try {
// 何らかの処理
} catch (err) {
// エラー処理
console.error('エラーが発生しました:', err);
// エラーを再スロー
throw err;
}
このパターンでは、catch
ブロックがエラーをキャッチし、ログ記録などの処理を行った後、同じエラーをthrow
を使って再スローします。これにより、エラーはさらに上位のコード(例えば、他のtry...catch
ブロックや呼び出し元の関数)に伝播され、そこで適切にハンドリングされることになります。
throw new Error と throw err の違い
-
throw new Error('メッセージ')
: 新しいエラーオブジェクトを作成してスローします。これは新たなエラーコンテキストを作成し、独自のエラーメッセージを指定する際に使用されます。 -
throw err
: 既にキャッチされたエラーオブジェクトをそのまま再スローします。これは、エラーの伝播を維持しながら、追加の処理(ログ記録など)を行う場合に使用されます。
throw err
- 使用する場合: 既にキャッチされたエラーオブジェクトをそのまま上位のコードに伝播させたい場合。
- 目的: エラーの原因やスタックトレースを変更せずに、そのままエラーを伝播させます。これにより、エラーが最初に発生した場所の情報が保持されます。
throw new Error
- 使用する場合: 新しいエラーコンテキストを作成したい、またはエラーメッセージをより明確にしたい場合。
- 目的: 新しいエラーオブジェクトを作成することで、エラーの説明をカスタマイズできます。ただし、新しいエラーオブジェクトを作成すると、元のエラーのスタックトレースは失われます。
使い分けのポイント
- エラーの詳細(原因や場所)をそのまま保持する必要がある場合は
throw err
を使用します。 - エラーの説明をカスタマイズしたい、またはエラーの文脈を変更したい場合は
throw new Error('カスタムメッセージ')
を使用します。
まとめ
エラーハンドリングの際には、エラーの原因となったコンテキスト、エラーメッセージの明確さ、およびエラー情報の伝播方法を考慮して、throw err
とthrow new Error
のどちらを使用するかを決定します。これにより、エラーをより適切に処理し、開発者やエンドユーザーに必要な情報を提供できます。