- RSS フィードを購読する
- 新着としてマーク
- 既読としてマーク
- ブックマーク
- 購読
- 印刷用ページ
- 不適切なコンテンツを報告
問題:JSON では不十分な場合
- イメージストレージ:Replicate AI で AI が生成したアバターが、添付ファイルとして保存する必要があるバイナリ画像 URL として返されました
- 安全なイメージサービング:レコード ACL を変更せずに画像にアクセスできるユーザーをきめ細かく制御する必要がありました
- データエクスポート:当社の印刷ベンダーは、プロフェッショナルなカード印刷プロセスのために、画像 URL を含む CSV エクスポートを必要としていました
テクニック 1: バイナリ ファイルを取得して添付ファイルとして保存する
saveImageAsAttachment: function ({ imageUrl, tableName, recordSysId, fieldOrFileName }) {
const request = new sn_ws.RESTMessageV2();
request.setHttpMethod('GET');
request.setEndpoint(imageUrl);
request.saveResponseBodyAsAttachment(tableName, recordSysId, fieldOrFileName);
const attachmentId = request.execute().getResponseAttachmentSysid();
return attachmentId;
}
この機能とは何ですか?
saveResponseBodyAsAttachment()
メソッドは、バイナリファイル処理に非常に効果的です。
- バイナリ画像データをフェッチするためのGETリクエストを実行します。
- base64エンコーディングを扱うことなく、バイナリストリームを自動的に処理します。
- sys_attachmentテーブルに添付ファイルレコードを作成します。
- レコードで参照できるように、添付ファイルのsys_idを返します。
その利点
- base64 オーバーヘッドなし:直接バイナリ転送は、画像データのbase64エンコーディングよりも約33〜37%小さくなります
- 1 行のソリューション:バイナリデータ転送と添付ファイル作成のすべての複雑さを処理します
- コンテンツタイプの自動検出:ServiceNow はファイルコンテンツから MIME タイプを特定します
テクニック 2: バイナリファイルを提供する
(function process(request, response) {
const parentId = request.queryParams.parentId;
const type = request.queryParams.type;
var attachmentGr = new GlideRecord('sys_attachment');
if (type == 'avatar_without_bg') {
let qc = attachmentGr.addQuery('table_name', 'x_snc_cctcg_photo_profile');
qc.addOrCondition('table_name', 'ZZ_YYx_snc_cctcg_photo_profile');
attachmentGr.addQuery('file_name', 'avatar_without_bg');
}
else if (type == 'qr_code') {
let qc = attachmentGr.addQuery('table_name', 'x_snc_cctcg_photo_qr_code');
qc.addOrCondition('table_name', 'ZZ_YYx_snc_cctcg_photo_qr_code');
attachmentGr.addQuery('file_name', 'qr_code');
}
// ... additional type handling
attachmentGr.addQuery('table_sys_id', parentId);
attachmentGr.query();
if (attachmentGr.next()) {
const attachment = new GlideSysAttachment();
const attachmentContentStream = attachment.getContentStream(attachmentGr.sys_id.toString());
response.setHeader('Content-Disposition', `filename="${parentId}_${type}"`);
response.setContentType('image/png');
response.setStatus(200);
const writer = response.getStreamWriter();
writer.writeStream(attachmentContentStream);
}
})(request, response);
バイナリストリームプロセスの分解
var attachmentGr = new GlideRecord('sys_attachment');
// Custom logic to find the right attachment based on type and parent ID
getContentStream()
メソッドは、バイナリファイルデータを表すJavaのGlideScriptableInputStreamを返します。
const attachment = new GlideSysAttachment();
const attachmentContentStream = attachment.getContentStream(attachmentGr.sys_id.toString());
Content-Disposition
ヘッダーは、ダウンロードしたファイルのデフォルトファイル名を設定するために使用されます。これにより、ブラウザーから REST エンドポイントの URL に移動すると、ファイルは
Content-Disposition
ヘッダーで指定された名前で自動的にダウンロードされます。
response.setHeader('Content-Disposition', `filename="${parentId}_${type}"`);
response.setContentType('image/png');
response.setStatus(200);
const writer = response.getStreamWriter();
writer.writeStream(attachmentContentStream);
カスタムファイルサービングが役立つ理由
- カスタムロジック:レコードの状態に応じて異なる画像を表示するなどのカスタムロジックを実装し、そのロジックをサーバーに格納して一貫性を保つことができます。
- パブリックアクセスオプション:必要に応じて、テーブル ACL を変更することなく、認証なしで特定の画像を公開できます。
- カスタムヘッダー:キャッシュの動作、ファイル名、およびコンテンツタイプを希望どおりに制御します
- 使用状況のトラッキング:必要に応じて、特定の画像やファイルへのアクセスをログまたは監視します。
テクニック 3: 文字列ベースのファイル (CSV など) を作成する
(function process(request, response) {
var encodedQuery = request.queryParams.query || 'avatar_without_bg!=';
var profileGR = new GlideRecord('x_snc_cctcg_photo_profile');
profileGR.addEncodedQuery(encodedQuery);
profileGR.setLimit(10);
profileGR.query();
var csvContent = '"Name","Email","Alternate Email","Special Card","Badge 1","Badge 2","Badge 3","First ServiceNow Release","Flavored persona","Is MVP","Rank ID","Technical Superpower","Team","Avatar","Avatar without Bg","Physical Card","Digital Card","Physical QR Code","Digital QR Code"\n';
while (profileGR.next()) {
var qrGR = new GlideRecord('x_snc_cctcg_photo_qr_code');
var physicalQR = '';
var digitalQR = '';
// Get associated QR codes
qrGR.addQuery('profile', profileGR.getUniqueValue());
qrGR.query();
while (qrGR.next()) {
if (qrGR.qr_type.toString() == 'de165405476cee50e52659db416d436d') {
physicalQR = qrGR.sys_id.toString();
} else if (qrGR.qr_type.toString() == 'c4265405476cee50e52659db416d4371') {
digitalQR = qrGR.sys_id.toString();
}
}
// Build CSV row with image URLs
var row = [
'"' + profileGR.name.toString() + '"',
'"' + profileGR.email.toString() + '"',
// ... other fields
(!profileGR.avatar_without_bg.nil() ? '"' + gs.getProperty('glide.servlet.uri') + '/api/x_snc_cctcg_photo/creatorcon_c3_webhooks/image?parentId=' + profileGR.getUniqueValue() + '&type=avatar_without_bg"' : '""'),
(!profileGR.card_with_qr.nil() ? '"' + gs.getProperty('glide.servlet.uri') + '/api/x_snc_cctcg_photo/creatorcon_c3_webhooks/image?parentId=' + profileGR.getUniqueValue() + '&type=card_physical"' : '""'),
// ... more image URL fields
].join(',');
csvContent += row + '\n';
}
// Output CSV content
response.setHeader('Content-Type', 'text/csv');
response.setHeader('Content-Disposition', 'attachment; filename=card_export.csv');
response.setStatus(200);
response.getStreamWriter().writeString(csvContent);
})(request, response);
CSV エクスポートの内訳
response.setHeader('Content-Type', 'text/csv');
response.setHeader('Content-Disposition', 'attachment; filename=card_export.csv');
// Build CSV row with image URLs
var row = [
'"' + profileGR.name.toString() + '"',
'"' + profileGR.email.toString() + '"',
// ... other fields
(!profileGR.avatar_without_bg.nil() ? '"' + gs.getProperty('glide.servlet.uri') + '/api/x_snc_cctcg_photo/creatorcon_c3_webhooks/image?parentId=' + profileGR.getUniqueValue() + '&type=avatar_without_bg"' : '""'),
(!profileGR.card_with_qr.nil() ? '"' + gs.getProperty('glide.servlet.uri') + '/api/x_snc_cctcg_photo/creatorcon_c3_webhooks/image?parentId=' + profileGR.getUniqueValue() + '&type=card_physical"' : '""'),
// ... more image URL fields
].join(',');
var csvContent = '"Name","Email","Alternate Email","Special Card","Badge 1","Badge 2","Badge 3","First ServiceNow Release","Flavored persona","Is MVP","Rank ID","Technical Superpower","Team","Avatar","Avatar without Bg","Physical Card","Digital Card","Physical QR Code","Digital QR Code"\n';
// Other logic
// For each row
csvContent += row + '\n';
'"' + gs.getProperty('glide.servlet.uri') + '/api/x_snc_cctcg_photo/creatorcon_c3_webhooks/image?parentId=' + profileGR.getUniqueValue() + '&type=avatar_without_bg"'
response.setBody()を使用する代わりに、CSV文字列を応答ストリームに直接書き込みます。
response.getStreamWriter().writeString(csvContent);
まとめ
- バイナリコンテンツをフェッチ、変換、提供するファイル処理ワークフロー (これには MID サーバーのヘルプが必要な場合があります)
- 特定の外部システム要件に合わせて調整されたカスタムエクスポート形式
- 従来のレコードベースの権限を超えた柔軟なセキュリティモデル
- 希望するデータ形式に関係なく、任意の外部システムで動作する統合パターン
この記事は機械翻訳されております。最新は元となる記事をご覧ください: https://www.servicenow.com/community/developer-advocate-blog/beyond-json-working-with-binary-files-a...。
免責事項: 一部の日本語は、翻訳ソフトウェアを使用してお客様の便宜のために翻訳されています。正確な翻訳をご提供できるよう相当な努力を払っておりますが、いかなる自動翻訳も人間の翻訳者に代わすることはなく、そのようなことは意図されておりません。翻訳は「現状のまま」提供されています。他言語への翻訳の的確性、信頼性または正確性については、明示または黙示を問わず、いかなる保証も行われません。翻訳ソフトには限界があるため、一部のコンテンツが正確に翻訳されていない場合があります。これらの資料の公用言語は英語です。翻訳の際に生じる相違または不一致は、コンプライアンスまたは履行の目的においては拘束力を有さず、法的効力はないものとします。
This article was machine translated and replicated from the original source: https://www.servicenow.com/community/developer-advocate-blog/beyond-json-working-with-binary-files-a...。