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