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 org.opengion.fukurou.util.StringUtil;
019import org.opengion.hayabusa.common.HybsSystem;
020import org.opengion.hayabusa.common.HybsSystemException;
021
022import java.io.ByteArrayInputStream;
023import java.io.File;
024import java.util.Base64;
025import java.awt.image.BufferedImage;
026import javax.imageio.ImageIO;
027
028import java.time.LocalDateTime;
029import java.time.format.DateTimeFormatter;
030
031import java.io.IOException;
032import jakarta.servlet.ServletException;
033import jakarta.servlet.ServletConfig;
034import jakarta.servlet.http.HttpServlet;
035import jakarta.servlet.http.HttpServletRequest;
036import jakarta.servlet.http.HttpServletResponse;
037// import jakarta.servlet.ServletOutputStream;                                                          // 8.0.0.0 (2021/07/31) Delete
038import jakarta.servlet.annotation.WebServlet;
039import jakarta.servlet.annotation.WebInitParam;
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        private String  saveDir = "jsp/snapshot/";
094//      private boolean isDebug = false;
095        private boolean isDebug ;                                               // 8.0.0.0 (2021/07/31) Avoid using redundant field initializer for ***
096
097        /**
098         * Servlet の 初期値設定を行います。
099         *
100         * WEB-INF/web.xml ファイルで、<servlet> タグ内で初期値設定を行います。
101         *       <init-param>
102         *           <param-name>saveDir</param-name>
103         *           <param-value>jsp/snapshot/</param-value>
104         *       </init-param>
105         *
106         * @param       config  ServletConfigオブジェクト
107         */
108        @Override
109        public void init( final ServletConfig config ) throws ServletException {
110                super.init( config );
111
112                saveDir = StringUtil.nval( config.getInitParameter("saveDir") , saveDir );
113
114                // boolean の StringUtil.nval は厳密チェックするので使わない。
115                isDebug = Boolean.parseBoolean( config.getInitParameter( "debug" ) );
116        }
117
118        /**
119         * GET メソッドが呼ばれたときに実行します。
120         *
121         * 処理は、doPost へ振りなおしています。
122         *
123         * @param       request HttpServletRequestオブジェクト
124         * @param       response        HttpServletResponseオブジェクト
125         *
126         * @og.rev 7.4.2.1 (2021/05/21) 新規追加
127         *
128         * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
129         * @throws IOException 入出力エラーが発生したとき
130         */
131        @Override
132        public void doGet( final HttpServletRequest request, final HttpServletResponse response )
133                                                        throws ServletException, IOException {
134                doPost( request,response );
135        }
136
137        /**
138         * POST メソッドが呼ばれたときに実行します。
139         *
140         * @param       request HttpServletRequestオブジェクト
141         * @param       response        HttpServletResponseオブジェクト
142         *
143         * @og.rev 7.4.2.1 (2021/05/21) 新規追加
144         *
145         * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
146         * @throws IOException 入出力エラーが発生したとき
147         */
148        @Override
149        public void doPost( final HttpServletRequest request, final HttpServletResponse response )
150                                                        throws ServletException, IOException {
151
152                // boolean の StringUtil.nval は厳密チェックするので使わない。
153                final String debugPrm = request.getParameter( "debug" ) ;
154                final boolean debug = StringUtil.isNull( debugPrm )
155                                                                        ? isDebug                                                               // 未指定なら、初期値を使う
156                                                                        : Boolean.parseBoolean( debugPrm );             // "true"以外はfalse になる。
157
158                String data = request.getParameter( "img" );
159                if( StringUtil.isNotNull( data ) ) {
160                        // Javascriptのcanvas.toDataURL()関数から派生したデータを保存したい場合は、
161                        // 空白をプラスに変換する必要があります。そうしないと、デコードされたデータが破損します。
162                        data = data.replace(' ','+');
163
164                        // 相対パスを絶対パスに変換。ファイルセパレータも正規化されています。
165                        final String dir = HybsSystem.url2dir( StringUtil.nval( request.getParameter( "dir" ),saveDir ) );
166                        if( debug ) { System.out.println( "dir=" + dir ); }
167
168                        // ファイル名が無ければ、現在時刻.png
169                        String file = request.getParameter( "file" );
170                        if( StringUtil.isNull( file ) ) {
171                                final LocalDateTime nowDateTime = LocalDateTime.now();
172                                file = nowDateTime.format( YMDH ) + ".png";
173                        }
174
175                        if( debug ) { System.out.println( "file=" + file ); }
176
177                        // Base64をデコードしてファイルに戻す。
178                        final byte[] bytes = Base64.getDecoder().decode(data);
179                        try( ByteArrayInputStream input = new ByteArrayInputStream(bytes) ) {
180                                final BufferedImage image = ImageIO.read(input);
181                                final File output = new File( dir, file );
182                                final File parent = output.getParentFile();
183                                if( parent != null && !parent.exists() ) {                      // parent は null があり得る。存在しない場合のみ。
184                                        if( !parent.mkdirs() ) {                // 8.0.0.0 (2021/07/31) spotbugs:例外的戻り値を無視
185                                                final String errMsg = "出力先フォルダが生成できませんでした。" + parent ;
186                                                throw new HybsSystemException( errMsg );
187                                        }
188                                }
189                                ImageIO.write(image, "png", output);
190                                if( debug ) { System.out.println( "output=" + output.getAbsolutePath() ); }
191                        }
192                        catch( final Throwable th ) {
193                                final String errMsg = "Base64 デコード処理が失敗しました。" ;
194                                throw new HybsSystemException( errMsg,th );
195                        }
196                }
197        }
198}