    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 *     &lt;servlet&gt;
    052 *         &lt;servlet-name&gt;imageSave&lt;/servlet-name&gt;
    053 *         &lt;servlet-class&gt;org.opengion.hayabusa.servlet.ImageSave&lt;/servlet-class&gt;
    054 *       &lt;init-param&gt;
    055 *           &lt;param-name&gt;saveDir&lt;/param-name&gt;
    056 *           &lt;param-value&gt;jsp/snapshot/&lt;/param-value&gt;
    057 *       &lt;/init-param&gt;
    058 *       &lt;init-param&gt;
    059 *           &lt;param-name&gt;debug&lt;/param-name&gt;
    060 *           &lt;param-value&gt;false&lt;/param-value&gt;
    061 *       &lt;/init-param&gt;
    062 *     &lt;/servlet&gt;
    063 *
    064 *     &lt;servlet-mapping&gt;
    065 *         &lt;servlet-name&gt;imageSave&lt;/servlet-name&gt;
    066 *         &lt;url-pattern&gt;/jsp/imageSave&lt;/url-pattern&gt;
    067 *     &lt;/servlet-mapping&gt;
    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 ファイルで、&lt;servlet&gt; タグ内で初期値設定を行います。
    101         *       &lt;init-param&gt;
    102         *           &lt;param-name&gt;saveDir&lt;/param-name&gt;
    103         *           &lt;param-value&gt;jsp/snapshot/&lt;/param-value&gt;
    104         *       &lt;/init-param&gt;
    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}