Notes開発者のためのXPagesデザインレシピ

簡単でCoolなXPagesアプリケーションを作るための情報を発信していきます

連続講座

ChatGPTとペアプログラミングで画像リサイズ機能を作ろう(実装編)

はじめに(共通)

このブログ記事は下記三部作の中編です。検索エンジンでたどり着いた方は他の関連記事も御覧下さい。

  1. ChatGPTとペアプログラミングで画像リサイズ機能を作ろう(実装検討編)
  2. ChatGPTとペアプログラミングで画像リサイズ機能を作ろう(実装編) ←ココ
  3. ChatGPTとペアプログラミングで画像リサイズ機能を作ろう(課題解決編)

今日は前回の実装検討結果を元にXPagesに実装していきます。

スクリプトライブラリを作成し、リサイズ判定関数を作る

  1. 「コード>スクリプトライブラリ」を選択し、[新規スクリプトライブラリ]ボタンで「ssjs_image_resize」を作成します。

名前

ssjs_image_resize

コメント

 

タイプ

サーバーJavaScript

  1. 作成したスクリプトライブラリ「ssjs_image_resize」にリサイズ対象判定(isResize)を追加し、保存します。
/*
 * リサイズ対象判定
 *  引数      :fileSize-ファイルサイズ(バイト)
 *  戻り値 :true-リサイズ対象、false-対象外
 */
function isResize(fileSize){
    var TARGET_SIZE = 1048576;
    
    var ret = false;
    if(fileSize >= TARGET_SIZE){
        ret = true;
    }
    return ret;
}

【解説】
リサイズ対象判定関数は、引数で受け取ったファイルサイズ(添付ファイルのファイルサイズ)が1MB(1048576kb)の場合にtrueを返します。この後、作成するリサイズ関数から呼び出し、trueの場合のみリサイズ処理を行います。
また、引数で受け取るのはファイルサイズではなく、ファイルオブジェクトそのものという考えもありますが、メモリ使用量を削減し、処理を単純化するためにファイルサイズを受け取っています。

画像ファイル判定関数を作る

  1. 作成したスクリプトライブラリ「ssjs_image_resize」に画像ファイル判定(isImageFile)を追加し、保存します。
/*
 * 拡張子が画像の場合、trueを返す
 *  引数      :fileName-ファイル名
 *  戻り値 :true-画像、false-画像でない
 */
function isImageFile(fileName){
    //画像ファイルの拡張子リストを作成
    var imageExtensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp"];
    // ファイル名から拡張子を抽出する
    var fileExtension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
    // 拡張子が画像ファイルの拡張子リストに含まれているかを確認する
    var ret = false;
    for(var i=0; i < imageExtensions.length; i++){
        if(imageExtensions[i] == fileExtension){
            ret = true;
            continue;
        }
    }
    return ret; 
}

【解説】
画像ファイル判定関数は、引数で受け取ったファイル名の拡張子が画像ファイルのものか?を判定し、一致する場合にtrueを返します。

リサイズ関数を作る

  1. 作成したスクリプトライブラリ「ssjs_image_resize」にリサイズ関数(resizeImage)を追加し、保存します。
/*
 * 文書に保存された画像ファイルをリサイズし、再添付する
 * 	引数		:xspdocument-現在の文書
 * 	戻り値	:false-画像圧縮せず終了
 */
function resizeImage(xspdocument){
	importPackage(java.io);
	importPackage(com.ibm.xsp.http);
	importPackage(java.awt);
	importPackage(java.awt.image);
	importPackage(javax.imageio);

	// 添付ファイルを取得するために保存を実行
	xspdocument.save();
		
	// リッチテキストフィールドを取得(添付ファイルを消すとリッチテキストフィールド自体が消えるので、nullの場合は処理を終了)
	var richTextField:NotesRichTextItem = xspdocument.getDocument().getFirstItem("ImageBody");
	if(richTextField == null){
		return false;
	}
	
	// リッチテキストフィールドからファイルオブジェクトを取得
	var files:java.util.Vector = richTextField.getEmbeddedObjects();
	if(files.isEmpty()){
		return false;
	}else{
		var filei:java.util.Iterator = files.iterator();
		while(filei.hasNext()){
			var eobj:NotesEmbeddedObject = filei.next();
			var fileName = eobj.getName();
				
			// 画像ファイル判定(画像以外は何もしない)
			if(!isImageFile(fileName)){
				continue;
			}
			
			// ファイルサイズ判定(小さすぎるファイルは何もしない)
			if(!isResize(eobj.getFileSize())){
				continue;
			}
			
			// リッチテキストからファイルを取得
			var fileData:com.ibm.xsp.http.IUploadedFile = richTextField.getEmbeddedObject(fileName);

			// 縮小率(元画像の30%のサイズに縮小する)
			var scalePercent = 30;
			// tempフォルダ
			var tmpFolder = "c:\\tmp\\";
			// ファイルオブジェクトがnullでない場合、処理を行う
			if (fileData != null) {
			    // IUploadedFileからInputStreamを取得
			    var inputStream = fileData.getInputStream();
				
			    // オリジナル画像を読み込む
			    var originalImage = ImageIO.read(inputStream);
				
			    // 縮小後の画像の幅と高さを計算
			    var newWidth = originalImage.getWidth() * scalePercent / 100;
			    var newHeight = originalImage.getHeight() * scalePercent / 100;
				    
			    // 縮小後の画像を作成
			    var scaledImage = originalImage.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
			    var resizedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
			    var g = resizedImage.createGraphics();
			    g.drawImage(scaledImage, 0, 0, null);
			    g.dispose();    
				    		    
			    // ファイルを保存するためのOutputStreamを作成
			    var outputStream = new FileOutputStream(tmpFolder + fileName);
				
			    // JPEG形式で保存
			    ImageIO.write(resizedImage, "jpg", outputStream);
				
			    // ストリームをクローズ
			    outputStream.close();
			    inputStream.close();
				    
			    // 既存の添付ファイルを削除
				eobj.remove();
							    
			    // リサイズした画像をアップロード
			    var attachment: EmbeddedObject = richTextField.embedObject(1454, "", tmpFolder + fileName, "");
				
				// リサイズした画像を削除
				var deleteFile = new File(tmpFolder + fileName);
				if (deleteFile.exists()) {
					deleteFile["delete"]();				// メソッド名をdelete()として呼び出し
				}
			}
		}
	}
}

【解説】
リサイズ関数は、文書(document1)からリッチテキストフィールド(ImageBody)を取得し、添付されたファイルを順に処理していきます。
リサイズ条件に一致する画像は、元画像の30%にリサイズされた上で、サーバー上のテンポラリフォルダ(c:\tmp)に保存された後、リッチテキストファイルの元画像と差し替えられます。
テンポラリフォルダ、縮小サイズなどは必要に応じて変更して下さい。

保存ボタンに作成したリサイズ関数を追加する

  1. フォーム用スクリプトライブラリ「ssjs_frm_dailyreport」を開き、先頭で作成したスクリプトライブラリをインポートします。インポートする場所は、それぞれの開発環境に応じて変更して下さい。
import ssjs_image_resize;

【解説】
SSJSでimportせずに、呼び出し先のXPages側でリソースとして呼び出しても同じことができますが、今回の関数は保存ボタン関数から呼び出す形のため、SSJS側でimpoprtした方がエレガントな書き方になると思います。

  1. 保存処理の前に作成したリサイズ関数を追加します。必要な部分は「resizeImage(document1);」だけですので、御自身の環境に合わせて追加して下さい。
/*
 * button event
 */
// Save
function btnSave_Click(){
	try{
		// Modified Check
		if(checkModified()){
			var errMsg 	= "別のユーザーが編集を行いました。一度文書を閉じ、再度編集を行って下さい。"
			viewScope.errMsg = "<div class='alert alert-danger'>"+errMsg+"</div>";
			return;
		}
		
		// Input Check & save
		var errMsg = "";
		errMsg = @If(@IsNull(@GetField("Subject")),"件名を入力して下さい。<br>","");
		errMsg = errMsg + @If(@IsNull(@GetField("Category")),"カテゴリを入力して下さい。<br>","");
		errMsg = errMsg + @If(@IsNull(@GetField("Memo")),"本文を入力して下さい。<br>","");
		
		if(errMsg!=""){
			viewScope.errMsg = "<div class='alert alert-danger'>" + errMsg + "</div>";
		}else{
			resizeImage(document1);
			document1.save();
			context.redirectToPrevious()
		}
	}catch(e){
		print("btnSave_Click Error:" + e);
	}
}


完成と課題

新規作成時、文書更新時に画像ファイルを追加し、リサイズが正常に動作することを確認します。

これで完成かというと、環境によって下記の問題が発生することがわかっています。

■わかっている課題

  1. ファイルのダウンロード時に拡張子がjpgではなく、jfifになってしまう。
  2. 画像ファイルを複数添付した際にメモリエラーで落ちてしまう。
  3. 画像ファイルを複数添付した際の待ち時間中に他のボタンを押せてしまう。

明日は、上記課題の原因と解決策を解説します。