001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.hayabusa.servlet; 017 018import java.io.ByteArrayInputStream; 019import java.io.File; 020import java.util.Base64; 021import java.awt.image.BufferedImage; 022import javax.imageio.ImageIO; 023 024import java.time.LocalDateTime; 025import java.time.format.DateTimeFormatter; 026 027import java.io.IOException; 028import jakarta.servlet.ServletException; 029import jakarta.servlet.ServletConfig; 030import jakarta.servlet.http.HttpServlet; 031import jakarta.servlet.http.HttpServletRequest; 032import jakarta.servlet.http.HttpServletResponse; 033// import jakarta.servlet.ServletOutputStream; // 8.0.0.0 (2021/07/31) Delete 034import jakarta.servlet.annotation.WebServlet; 035import jakarta.servlet.annotation.WebInitParam; 036 037import org.opengion.fukurou.util.StringUtil; 038import org.opengion.hayabusa.common.HybsSystem; 039import org.opengion.hayabusa.common.HybsSystemException; 040 041/** 042 * クライアントからBase64でエンコードして送信された画像ファイルを、 043 * ファイルに変換してセーブするサーブレットです。 044 * 045 * 想定される使い方は、クライアント側で、カメラ等で撮影された映像を 046 * canvasに書き込み、それを、Base64でPOSTする感じです。 047 * 048 * 一般的なサーブレットと同様に、デプロイメント・ディスクリプタ WEB-INF/web.xml に、 049 * servlet 要素と そのマッピング(servlet-mapping)を定義する必要があります。 050 * 051 * <servlet> 052 * <servlet-name>imageSave</servlet-name> 053 * <servlet-class>org.opengion.hayabusa.servlet.ImageSave</servlet-class> 054 * <init-param> 055 * <param-name>saveDir</param-name> 056 * <param-value>jsp/snapshot/</param-value> 057 * </init-param> 058 * <init-param> 059 * <param-name>debug</param-name> 060 * <param-value>false</param-value> 061 * </init-param> 062 * </servlet> 063 * 064 * <servlet-mapping> 065 * <servlet-name>imageSave</servlet-name> 066 * <url-pattern>/jsp/imageSave</url-pattern> 067 * </servlet-mapping> 068 * 069 * 一般には、http://サーバー:ポート/システムID/jsp/imageSave 070 * 引数;img=イメージファイル 071 * dir=ディレクトリ (初期値は、saveDir="jsp/snapshot/") 072 * file=ファイル名 (初期値は、filePtn="yyyyMMddHHmmssSSS" + ".png"(固定)) 073 * 形式のURL でPOSTします。 074 * 075 * @og.rev 7.4.2.1 (2021/05/21) 新規追加 076 * @og.group その他機能 077 * 078 * @version 7.4 079 * @author Kazuhiko Hasegawa 080 * @since JDK11, 081 */ 082@WebServlet( 083 urlPatterns = "/jsp/imageSave" , 084 initParams = { 085 @WebInitParam( name="saveDir" , value="jsp/snapshot/" ) 086 } 087) 088public class ImageSave extends HttpServlet { 089 private static final long serialVersionUID = 742120210521L ; 090 091 private final static DateTimeFormatter YMDH = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS" ); 092 093 /** セーブディレクトリ */ 094 private String saveDir = "jsp/snapshot/"; 095// private boolean isDebug = false; 096 /** デバッグフラグ */ 097 private boolean isDebug ; // 8.0.0.0 (2021/07/31) Avoid using redundant field initializer for *** 098 099 /** 100 * デフォルトコンストラクター 101 * 102 * @og.rev 8.5.3.2 (2023/10/13) JDK21対応。警告: デフォルトのコンストラクタの使用で、コメントが指定されていません 103 */ 104 public ImageSave() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 105 106 /** 107 * Servlet の 初期値設定を行います。 108 * 109 * WEB-INF/web.xml ファイルで、<servlet> タグ内で初期値設定を行います。 110 * <init-param> 111 * <param-name>saveDir</param-name> 112 * <param-value>jsp/snapshot/</param-value> 113 * </init-param> 114 * 115 * @param config ServletConfigオブジェクト 116 */ 117 @Override 118 public void init( final ServletConfig config ) throws ServletException { 119 super.init( config ); 120 121 saveDir = StringUtil.nval( config.getInitParameter("saveDir") , saveDir ); 122 123 // boolean の StringUtil.nval は厳密チェックするので使わない。 124 isDebug = Boolean.parseBoolean( config.getInitParameter( "debug" ) ); 125 } 126 127 /** 128 * GET メソッドが呼ばれたときに実行します。 129 * 130 * 処理は、doPost へ振りなおしています。 131 * 132 * @param request HttpServletRequestオブジェクト 133 * @param response HttpServletResponseオブジェクト 134 * 135 * @og.rev 7.4.2.1 (2021/05/21) 新規追加 136 * 137 * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。 138 * @throws IOException 入出力エラーが発生したとき 139 */ 140 @Override 141 public void doGet( final HttpServletRequest request, final HttpServletResponse response ) 142 throws ServletException, IOException { 143 doPost( request,response ); 144 } 145 146 /** 147 * POST メソッドが呼ばれたときに実行します。 148 * 149 * @param request HttpServletRequestオブジェクト 150 * @param response HttpServletResponseオブジェクト 151 * 152 * @og.rev 7.4.2.1 (2021/05/21) 新規追加 153 * 154 * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。 155 * @throws IOException 入出力エラーが発生したとき 156 */ 157 @Override 158 public void doPost( final HttpServletRequest request, final HttpServletResponse response ) 159 throws ServletException, IOException { 160 161 // boolean の StringUtil.nval は厳密チェックするので使わない。 162 final String debugPrm = request.getParameter( "debug" ) ; 163 final boolean debug = StringUtil.isNull( debugPrm ) 164 ? isDebug // 未指定なら、初期値を使う 165 : Boolean.parseBoolean( debugPrm ); // "true"以外はfalse になる。 166 167 String data = request.getParameter( "img" ); 168 if( StringUtil.isNotNull( data ) ) { 169 // Javascriptのcanvas.toDataURL()関数から派生したデータを保存したい場合は、 170 // 空白をプラスに変換する必要があります。そうしないと、デコードされたデータが破損します。 171 data = data.replace(' ','+'); 172 173 // 相対パスを絶対パスに変換。ファイルセパレータも正規化されています。 174 final String dir = HybsSystem.url2dir( StringUtil.nval( request.getParameter( "dir" ),saveDir ) ); 175 if( debug ) { System.out.println( "dir=" + dir ); } 176 177 // ファイル名が無ければ、現在時刻.png 178 String file = request.getParameter( "file" ); 179 if( StringUtil.isNull( file ) ) { 180 final LocalDateTime nowDateTime = LocalDateTime.now(); 181 file = nowDateTime.format( YMDH ) + ".png"; 182 } 183 184 if( debug ) { System.out.println( "file=" + file ); } 185 186 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 187 String errMsg = null; 188 189 // Base64をデコードしてファイルに戻す。 190 final byte[] bytes = Base64.getDecoder().decode(data); 191 try( ByteArrayInputStream input = new ByteArrayInputStream(bytes) ) { 192 final BufferedImage image = ImageIO.read(input); 193 final File output = new File( dir, file ); 194 final File parent = output.getParentFile(); 195 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CollapsibleIfStatements 196// if( parent != null && !parent.exists() ) { // parent は null があり得る。存在しない場合のみ。 197// if( !parent.mkdirs() ) { // 8.0.0.0 (2021/07/31) spotbugs:例外的戻り値を無視 198 if( parent != null && !parent.exists() && !parent.mkdirs() ) { // parent は null があり得る。存在しない場合のみ。 199 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 200// final String errMsg = "出力先フォルダが生成できませんでした。" + parent ; 201// throw new HybsSystemException( errMsg ); 202 errMsg = "出力先フォルダが生成できませんでした。" + parent ; 203// } 204 } 205 else { 206 ImageIO.write(image, "png", output); 207 if( debug ) { System.out.println( "output=" + output.getAbsolutePath() ); } 208 } 209 } 210 catch( final Throwable th ) { 211 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 212// final String errMsg = "Base64 デコード処理が失敗しました。" ; 213// throw new HybsSystemException( errMsg,th ); 214 errMsg = "Base64 デコード処理が失敗しました。" + th.getMessage(); 215 } 216 217 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 218 if( errMsg != null ) { 219 throw new HybsSystemException( errMsg ); 220 } 221 } 222 } 223}