本記事では、MLE JavaScriptを使った例として、JPEG画像のリサイズを行なうアプリケーションを作成します。JPEG画像のデコード、エンコードおよびリサイズはMLEのJavaScriptで実行します。
作成したアプリケーションは以下のように動作します。
以下より作成手順を紹介します。
最初にJPEG画像を操作するMLEモジュールを作成します。
SQLワークショップのオブジェクト・ブラウザを開きます。
最初にnpmのjpeg-jsを元にしたMLEモジュールを作成します。
https://cdn.jsdelivr.net/npm/jpeg-js@0.4.4/+esm
MLEモジュールの作成をクリックします。
MLEモジュールESM_JPEG_JS_0_4_4が作成されます。
MLEモジュールの作成を続けます。
最初にnpmのbufferを元にしたMLEモジュールを作成します。
名前はESM_BUFFER_6_0_3、バージョンは6.0.3、ソース・タイプにURLを選択し、URLとして以下を指定します。
https://cdn.jsdelivr.net/npm/buffer@6.0.3/+esm
https://cdn.jsdelivr.net/npm/buffer@6.0.3/+esm
モジュールbufferが参照しているモジュールbase64-jsとieee754を、それぞれMLEモジュールとして作成します。
base64-jsのMLEモジュールを作成します。
名前はESM_BASE64_JS_1_5_1、バージョンは1.5.1、ソース・タイプにURLを選択し、URLとして以下を指定します。
https://cdn.jsdelivr.net/npm/base64-js@1.5.1/+esm
https://cdn.jsdelivr.net/npm/base64-js@1.5.1/+esm
ieee754のMLEモジュールを作成します。
名前はESM_IEEE754_1_2_1、バージョンは1.2.1、ソース・タイプにURLを選択し、URLとして以下を指定します。
https://cdn.jsdelivr.net/npm/ieee754@1.2.1/+esm
https://cdn.jsdelivr.net/npm/ieee754@1.2.1/+esm
画像のリサイズを行なうMLEモジュールを作成します。
名前はRESIZE、ソース・タイプにURLを選択し、URLとして以下を指定します。
https://cdn.jsdelivr.net/npm/@jimp/plugin-resize@0.22.12/es/modules/resize.js
作成したMLEモジュールをインポートとして含むMLE環境を作成します。
MLE環境の作成をクリックします。
MLE環境IMAGE_ENVが作成されます。インポートの追加をクリックします。
モジュール所有者にAPEXワークスペース・スキーマを選択します。
モジュール名としてESM_JPEG_JS_0_4_4を選択します。インポート名は/npm/jpeg-js@0.4.4/+esmとします。
作成をクリックします。
同様に、以下のモジュール名とインポート名の組みを作成します。インポート名は、jsDelivr.comによって自動生成されたESモジュールに含まれるimport文の参照と一致する必要があります。モジュールresizeは自動生成されたコードではないため、インポート名はそのままresizeとしています。
モジュール名:ESM_BUFFER_6_0_3、インポート名:/npm/buffer@6.0.3/+esm
モジュール名:ESM_BASE64_JS_1_5_1、インポート名:/npm/base64-js@1.5.1/+esm
モジュール名:ESM_IEEE754_1_2_1、インポート名:/npm/ieee754@1.2.1/+esm
モジュール名:RESIZE、インポート名:resize
MLE環境IMAGE_ENVに5つのMLEモジュールがインポートとして登録されていれば、MLE環境の準備は完了です。
APEXのアプリケーションを作成します。
リサイズ前のオリジナルの画像とリサイズ後の画像を保存する表として、EBMJ_IMAGESを作成します。以下のDDLを実行します。
create table ebmj_images (
id number generated by default on null as identity
constraint ebmj_images_id_pk primary key,
title varchar2(80 char) not null,
image blob,
image_mimetype varchar2(80 char),
image2 blob,
image2_mimetype varchar2(80 char)
);
アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。名前はImage Resizeとします。
表EBMJ_IMAGESをソースとしたフォーム付き対話モード・レポートのページを作成します。
ページ・タイプとして対話モード・レポートを選択します。
対話モード・レポートのページ番号は2、名前はImagesとします。フォーム・ページを含めるにチェックを入れます。
フォーム・ページ番号は3、フォーム・ページ名はImage、フォーム・ページ・モードはドロワーを選択します。
データ・ソースの表/ビューの名前としてEBMJ_IMAGESを選択します。
次へ進みます。
主キー列1としてID (Number)を選択します。
ページの作成を実行します。
対話モード・レポートとフォームのページが作成されます。
ページ・デザイナで対話モード・レポートのページを開き、画像を保存しているBLOB列の属性を調整します。
列IMAGEを選択し、BLOB属性のMIMEタイプ列としてIMAGE_MIMETYPEを設定します。
ページ・デザイナでフォームのページを開きます。
ページ・アイテムP3_IMAGEを選択し、ストレージのMIMEタイプ列としてIMAGE_MIMETYPEを設定します。
Imagesのページを開き、作成をクリックします。
ドロワーが開きます。Titleを入力し、ImageにJPEG画像を選択します。
作成をクリックします。
ページ・デザイナでホーム・ページを開きます。画像のリサイズを行なうUIを実装します。
リサイズの元画像を選択するページ・アイテムを作成します。
識別の名前はP1_ID、タイプは選択リスト、ラベルはImageとします。設定の選択時のページ・アクションとして値のリダイレクトと設定を選択します。
LOVのタイプはSQL問合せ、SQL問合せとして以下を記述します。
select title d, id r from ebmj_images
追加値の表はオフ、NULL値の表示はオン、NULL表示値として- Select Image -を記述します。
セッション・ステートのストレージはセッションごと(永続)を選択します。
縮小する比率を指定するページ・アイテムを作成します。例えば10を指定すると、画像の横と縦がそれぞれ1/10となり、面積としては1/100になります。
識別の名前はP1_SCALE、タイプは数値フィールド、ラベルはScaleとします。外観のテンプレートとしてRequired - Floartingを選択します。検証の必須の値をオン、デフォルトのタイプを静的とし、静的値に10を設定します。セッション・ステートのストレージとしてセッションごと(永続)を選択します。
識別のボタン名はRESIZE、ラベルはResizeとします。外観のホットはオン、テンプレート・オプションのWidthにStretchを選択します。
動作のアクションはデフォルトのページの送信です。
リサイズ前の画像を表示するページ・アイテムを作成します。
識別の名前はP1_IMAGE、タイプはイメージの表示、ラベルはOriginalとします。設定の基準としてSQL文で戻されたBLOB列を選択し、SQL文として以下を記述します。
select image from ebmj_images where id = :P1_ID
セッション・ステートのストレージはリクエストごと(メモリーのみ)とします。
リサイズ後の画像を表示するページ・アイテムを作成します。
識別の名前はP1_IMAGE2、タイプはイメージの表示、ラベルはResizedとします。設定の基準としてSQL文で戻されたBLOB列を選択し、SQL文として以下を記述します。
select image2 from ebmj_images where id = :P1_ID
セッション・ステートのストレージはリクエストごと(メモリーのみ)とします。
JPEG画像のリサイズを行なうプロセスを作成します。
識別の名前はResize JPEG、タイプはコードの実行です。
ソースの言語にJavaScript (MLE)を選択し、JavaScriptコードとして以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const jpeg = await import('/npm/jpeg-js@0.4.4/+esm'); | |
const { Buffer } = await import('/npm/buffer@6.0.3/+esm'); | |
const { default:Resize } = await import('resize'); | |
const id = Number(apex.env.P1_ID); | |
const scale = Number(apex.env.P1_SCALE); | |
/* | |
* Extract original image as Oracle BLOB - blobImageOrg | |
*/ | |
const query = 'select image, image_mimetype from ebmj_images where id = :id'; | |
const res = session.execute(query, | |
{ | |
id: { type: oracledb.NUMBER, dir: oracledb.BIND_IND, val: id } | |
}, | |
{ | |
fetchInfo: { | |
"IMAGE": { type: oracledb.ORACLE_BLOB }, | |
"IMAGE_MIMETYPE": { type: oracledb.DB_TYPE_VARCHAR } | |
}, | |
outFormat: oracledb.OUT_FORMAT_OBJECT | |
} | |
); | |
if ( res.rows.length === 0 ) { | |
console.log("no rows selected."); | |
// do nothing. | |
return; | |
}; | |
const row = res.rows[0]; | |
const blobImageOrg = row.IMAGE; | |
let mimeType = row.IMAGE_MIMETYPE; | |
const blobImageOrgLen = blobImageOrg.length(); | |
console.log("Lenght of BLOB Image Original = ", blobImageOrgLen); | |
/* | |
* Read JPEG image from BLOB into Uint8Array then convert it into Buffer - jpegImageOrg | |
*/ | |
blobImageOrg.open(OracleBlob.LOB_READONLY); | |
let u8a = blobImageOrg.read(blobImageOrgLen, 1); | |
blobImageOrg.close(); | |
console.log("Length of Uint8Array read from BLOB = ", u8a.length); | |
const bufImageOrg = Buffer.from(u8a); | |
/* | |
* Decode JPEG image into RAW image - rawImageOrg | |
*/ | |
let rawImageOrg = null; | |
switch( mimeType ) { | |
case "image/jpeg": | |
rawImageOrg = jpeg.decode(bufImageOrg); | |
break; | |
default: | |
// do nothing. | |
return; | |
}; | |
/* | |
* Resize rawImageOrg into rawImageResized. | |
*/ | |
const originalWidth = rawImageOrg.width; | |
const originalHeight = rawImageOrg.height; | |
const targetWidth = Math.ceil( originalWidth / scale ); | |
const targetHeight = Math.ceil( originalHeight / scale ); | |
const resize = new Resize( | |
originalWidth, | |
originalHeight, | |
targetWidth, | |
targetHeight, | |
true, | |
true, | |
null, | |
); | |
const buf = resize.resizeHeight(resize.resizeWidth(rawImageOrg.data)); | |
const rawImageResized = { width: targetWidth, height: targetHeight, data: buf }; | |
/* | |
* Encode RAW image that is resized into JPEG image. | |
*/ | |
let bufImageResized = null; | |
switch( mimeType ) { | |
case "image/jpeg": | |
bufImageResized = jpeg.encode(rawImageResized, 10); | |
u8a = Uint8Array.from(bufImageResized.data); | |
break; | |
default: | |
// do nothing. | |
return; | |
}; | |
console.log("Lenth of u8a written to BLOB = ", u8a.length); | |
/* | |
* Write Uint8Array to Blob | |
*/ | |
const blobImageResized = OracleBlob.createTemporary(false); | |
blobImageResized.open(OracleBlob.LOB_READWRITE); | |
blobImageResized.write(1, u8a); | |
blobImageResized.close(); | |
console.log("Lenght of BLOB Image Resized = ", blobImageResized.length()); | |
/* | |
* Update Resized Image | |
*/ | |
const update = 'update ebmj_images set image2 = :blob, image2_mimetype = :mimeType where id = :id'; | |
const result = session.execute(update, | |
{ | |
id: { | |
type: oracledb.NUMBER, dir: oracledb.BIND_IND, val: id | |
}, | |
mimeType: { | |
type: oracledb.DB_TYPE_VARCHAR, dir: oracledb.BIND_IND, val: mimeType | |
}, | |
blob: { | |
type: oracledb.ORACLE_BLOB, dir: oracledb.BIND_IND, val: blobImageResized | |
} | |
} | |
); | |
blobImageResized.freeTemporary(); |
サーバー側の条件のボタン押下時にRESIZEを指定します。
最後に、アプリケーション定義のセキュリティを開き、データベース・セッションのMLE環境としてIMAGE_ENVを設定します。
変更の適用をクリックすると、アプリケーションは完成です。
アプリケーションを実行すると、記事の先頭にあるGIF動画のように動作します。
GIF動画ではScaleの10を指定し、横と縦の幅がそれぞれ1/10となるように設定していますが、それほど小さくなっていないように見えます。これは、元の画像がブラウザの画面サイズに合わせて縮小されて表示されているためです。
今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/mle-image-resize.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完