    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.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    019import org.opengion.hayabusa.common.HybsSystem;
    020import org.opengion.hayabusa.servlet.multipart.MultipartParser;
    021import org.opengion.hayabusa.servlet.multipart.Part;
    022import org.opengion.hayabusa.servlet.multipart.FilePart;
    023import org.opengion.hayabusa.servlet.multipart.ParamPart;
    024import org.opengion.fukurou.util.ZipArchive;                            // 6.0.0.0 (2014/04/11) zip 対応
    025
    026import org.opengion.fukurou.model.FileOperation;                        // 8.0.0.0 (2021/09/30)
    027import org.opengion.hayabusa.io.HybsFileOperationFactory;       // 8.0.0.0 (2021/09/30)
    028
    029import java.io.File;
    030import java.io.IOException;
    031// import java.io.FileNotFoundException;                                        // 6.9.0.1 (2018/02/05)
    032import java.util.Map;
    033import java.util.concurrent.ConcurrentSkipListMap;                      // 6.4.3.1 (2016/02/12) refactoring
    034
    035import java.util.List;
    036import java.util.ArrayList;
    037import java.util.Set;
    038import java.util.Random ;
    039import java.util.concurrent.atomic.AtomicInteger;                       // 5.5.2.6 (2012/05/25) findbugs対応
    040import jakarta.servlet.http.HttpServletRequest;
    041
    042/**
    043 * ﾌｧｲﾙをｻｰﾊﾞｰにｱｯﾌﾟﾛｰﾄﾞする場合に使用されるﾏﾙﾁﾊﾟｰﾄ処理ｻｰﾌﾞﾚｯﾄです｡
    044 *
    045 * 通常のﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞ時の､form で使用する､enctype="multipart/form-data"
    046 * を指定した場合の､他のﾘｸｴｽﾄ情報も､取り出すことが可能です｡
    047 *
    048 * ﾌｧｲﾙをｱｯﾌﾟﾛｰﾄﾞ後に､指定のﾌｧｲﾙ名に変更する機能があります｡
    049 * file 登録ﾀﾞｲｱﾛｸﾞで指定した name に､"_NEW" という名称を付けたﾘｸｴｽﾄ値を
    050 * ﾌｧｲﾙのｱｯﾌﾟﾛｰﾄﾞと同時に送信することで､この名前にﾌｧｲﾙを付け替えます｡
    051 * また､ｱｯﾌﾟﾛｰﾄﾞ後のﾌｧｲﾙ名は､name 指定の名称で､取り出せます｡
    052 * ｸﾗｲｱﾝﾄから登録したｵﾘｼﾞﾅﾙのﾌｧｲﾙ名は､name に､"_ORG" という名称
    053 * で取り出すことが可能です｡
    054 *
    055 * maxPostSize : 最大転送ｻｲｽﾞ(Byte)を指定します｡ 0,またはﾏｲﾅｽで無制限です｡
    056 * useBackup   : ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞ時に､すでに同名のﾌｧｲﾙが存在した場合に､
    057 *               ﾊﾞｯｸｱｯﾌﾟ処理(renameTo)するかどうか[true/false]を指定します(初期値:false)
    058 *
    059 * ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞ時に､ｱｯﾌﾟﾛｰﾄﾞ先に､同名のﾌｧｲﾙが存在した場合は､既存機能は､そのまま
    060 * 置き換えていましたが､簡易ﾊﾞｰｼﾞｮﾝｱｯﾌﾟ機能として､useBackup="true" を指定すると､既存のﾌｧｲﾙを
    061 * ﾘﾈｰﾑして､ﾊﾞｯｸｱｯﾌﾟﾌｧｲﾙを作成します｡
    062 * ﾊﾞｯｸｱｯﾌﾟﾌｧｲﾙは､ｱｯﾌﾟﾛｰﾄﾞﾌｫﾙﾀﾞを基準として､_backup/ﾌｧｲﾙ名.拡張子_処理時刻のlong値.拡張子 になります｡
    063 * ｵﾘｼﾞﾅﾙのﾌｧｲﾙ名（拡張子付）を残したまま､"_処理時刻のlong値" を追加し､さらに､ｵﾘｼﾞﾅﾙの拡張子を追加します｡
    064 * ﾊﾞｯｸｱｯﾌﾟﾌｧｲﾙの形式は指定できません｡
    065 *
    066 * 5.7.1.2 (2013/12/20) zip 対応
    067 * filename 属性に､".zip" の拡張子のﾌｧｲﾙ名を指定した場合は､ｱｯﾌﾟﾛｰﾄﾞされた一連のﾌｧｲﾙを
    068 * ZIP圧縮します｡これは､ｱｯﾌﾟﾛｰﾄﾞ後の処理になります｡
    069 * ZIP圧縮のｵﾘｼﾞﾅﾙﾌｧｲﾙは､そのまま残ります｡
    070 * なお､ZIPﾌｧｲﾙは､useBackup属性を true に設定しても､無関係に､上書きされます｡
    071 *
    072 * 8.0.1.0 (2021/10/29) storageType → storage ､bucketName → bucket に変更
    073 * ×  storage  (初期値：ｼｽﾃﾑﾘｿｰｽのCLOUD_TARGET) → 廃止
    074 * ×  bucket   (初期値：ｼｽﾃﾑﾘｿｰｽのCLOUD_BUCKET) → 廃止
    075 *   useLocal (初期値：false)
    076 *
    077 * @og.rev 5.10.9.0 (2019/03/01) oota ｸﾗｳﾄﾞｽﾄﾚｰｼﾞ対応を追加｡(Fileｸﾗｽを拡張)
    078 * @og.group その他機能
    079 *
    080 * @version  4.0
    081 * @author       Kazuhiko Hasegawa
    082 * @since    JDK5.0,
    083 */
    084public final class MultipartRequest {
    085        private static AtomicInteger dumyNewFileCnt = new AtomicInteger(1);             // 5.5.2.6 (2012/05/25) findbugs対応
    086
    087        // 5.6.5.3 (2013/06/28) ｱｯﾌﾟﾛｰﾄﾞ時のﾀﾞﾐｰﾌｧｲﾙ名をもう少しだけﾗﾝﾀﾞﾑにする｡
    088        // 6.3.9.0 (2015/11/06) Variables should start with a lowercase character(PMD)
    089        private static final String RANDOM_KEY = new Random().nextInt( Integer.MAX_VALUE ) + "_" ;
    090
    091        /** 6.4.3.1 (2016/02/12) PMD refactoring. TreeMap → ConcurrentSkipListMap に置き換え｡  */
    092        private final Map<String,List<String>> paramMap = new ConcurrentSkipListMap<>();        // 6.4.3.1 (2016/02/12) ｿｰﾄします｡
    093
    094        // 5.7.1.1 (2013/12/13) HTML5 ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞの複数選択（multiple）対応
    095        private final List<UploadedFile> files                          = new ArrayList<>();                    // 5.7.1.1 (2013/12/13) HTML5対応
    096
    097        /**
    098         * MultipartRequest ｵﾌﾞｼﾞｪｸﾄを構築します｡
    099         *
    100         * 引数として､ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞ時の保存ﾌｫﾙﾀﾞ､最大ｻｲｽﾞ､ｴﾝｺｰﾄﾞ､
    101         * 新しいﾌｧｲﾙ名などを指定できます｡新しいﾌｧｲﾙ名は､ｱｯﾌﾟﾛｰﾄﾞされる
    102         * ﾌｧｲﾙが一つだけの場合に使用できます｡複数のﾌｧｲﾙを同時に変更したい
    103         * 場合は､ｱｯﾌﾟﾛｰﾄﾞﾙｰﾙにのっとり､ﾘｸｴｽﾄﾊﾟﾗﾒｰﾀで指定してください｡
    104         *
    105         * HTML5 では､ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞ時に､multiple 属性（inputﾀｸﾞのtype="file"）を
    106         * 付ける事で､ﾌｧｲﾙを複数選択できます｡
    107         * その場合は､inputのname属性は､一つなので､_NEW による名前の書き換えはできません｡
    108         *
    109         * @og.rev 3.8.1.3A (2006/01/30) 新ﾌｧｲﾙ名にｵﾘｼﾞﾅﾙﾌｧｲﾙ名の拡張子をｾｯﾄします
    110         * @og.rev 4.0.0.0 (2007/11/28) ﾒｿｯﾄﾞの戻り値をﾁｪｯｸします｡
    111         * @og.rev 5.5.2.6 (2012/05/25) findbugs対応｡staticﾌｨｰﾙﾄﾞへの書き込みに､AtomicInteger を利用します｡
    112         * @og.rev 5.6.5.3 (2013/06/28) useBackup引数追加
    113         * @og.rev 5.7.1.1 (2013/12/13) HTML5 ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞの複数選択（multiple）対応
    114         * @og.rev 5.7.1.2 (2013/12/20) zip 対応
    115         * @og.rev 5.7.4.3 (2014/03/28) zip 対応復活｡inputFilename のﾘｸｴｽﾄ変数処理追加
    116         * @og.rev 6.0.2.4 (2014/10/17) useBackup 修正｡_PFX(接頭辞) , _SFX(接尾辞) 機能を追加｡ﾌｧｲﾙ名にﾌｫﾙﾀﾞ指定可
    117         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え｡
    118         * @og.rev 5.9.25.0 (2017/10/06) ｸﾗｳﾄﾞｽﾄﾚｰｼﾞ利用処理追加
    119         * @og.rev 6.9.0.1 (2018/02/05) ﾌｧｲﾙをｾｰﾌﾞするﾃﾞｨﾚｸﾄﾘは､必要な場合のみ､作成します｡
    120         * @og.rev 5.10.9.0 (2019/03/01) ｸﾗｳﾄﾞｽﾄﾚｰｼﾞ対応を追加｡
    121         * @og.rev 8.0.0.2 (2021/10/15) ﾛｰｶﾙﾌｧｲﾙとｸﾗｳﾄﾞﾌｧｲﾙ間の移動
    122         * @og.rev 8.0.1.0 (2021/10/29) useLocal 属性を追加｡storageType , bucketName 削除
    123         *
    124         * @param       request HttpServletRequestｵﾌﾞｼﾞｪｸﾄ
    125         * @param       saveDirectory   ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞがあった場合の保存ﾌｫﾙﾀﾞ名
    126         * @param       maxPostSize             ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞ時の最大ﾌｧｲﾙｻｲｽﾞ(Byte)0,またはﾏｲﾅｽで無制限
    127         * @param       encoding                ﾌｧｲﾙのｴﾝｺｰﾄﾞ
    128         * @param       inputFilename   ｱｯﾌﾟﾛｰﾄﾞされたﾌｧｲﾙの新しい名前
    129         * @param       useBackup               ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞ時に､ﾊﾞｯｸｱｯﾌﾟ処理するかどうか[true/false/rename]を指定
    130         * @param       fileURL                 ｸﾗｳﾄﾞｽﾄﾚｰｼﾞ用のURL
    131//       * @param       storage                 ｸﾗｳﾄﾞﾌﾟﾗｸﾞｲﾝ名(ﾛｰｶﾙﾌｧｲﾙを強制する場合は､LOCAL を指定する)
    132//       * @param       bucket                  ﾊﾞｹｯﾄ名(ﾛｰｶﾙﾌｧｲﾙを強制する場合は､LOCAL を指定する)
    133         * @param       useLocal                強制的にﾛｰｶﾙﾌｧｲﾙを使用する場合､true にｾｯﾄします｡
    134         * @throws IOException 入出力ｴﾗｰが発生したとき
    135         * @throws IllegalArgumentException ｾｰﾌﾞﾃﾞｨﾚｸﾄﾘ に関係するｴﾗｰ
    136         */
    137        public MultipartRequest(final HttpServletRequest request,
    138                                                        final String    saveDirectory,
    139                                                        final int               maxPostSize,
    140                                                        final String    encoding,
    141                                                        final String    inputFilename,
    142                                                        final String    useBackup,                                                              // 6.0.2.4 (2014/10/17) true/false/rename
    143//                                                      final String    fileURL) throws IOException,IllegalArgumentException {          // (2017/10/06) 追加
    144                                                        final String    fileURL,
    145//                                                      final String    storage,
    146//                                                      final String    bucket ) throws IOException,IllegalArgumentException {          // 5.10.9.0 (2019/03/01) ADD
    147                                                        final boolean   useLocal ) throws IOException,IllegalArgumentException {        // 8.0.1.0 (2021/10/29)
    148
    149                if( request == null ) {
    150                        throw new IllegalArgumentException("request cannot be null");
    151                }
    152
    153//              // 6.9.0.1 (2018/02/05) ﾌｧｲﾙをｾｰﾌﾞするﾃﾞｨﾚｸﾄﾘは､必要な場合のみ､作成します｡
    154//              if( saveDirectory == null ) {
    155//                      throw new IllegalArgumentException("saveDirectory cannot be null");
    156//              }
    157//              // 5.5.2.6 (2012/05/25) 0,またはﾏｲﾅｽで無制限
    158//              // Save the dir
    159//              final File dir = new File(saveDirectory);
    160//
    161//              // Check saveDirectory is truly a directory
    162//              if( !dir.isDirectory() ) {
    163//                      throw new IllegalArgumentException("Not a directory: " + saveDirectory);
    164//              }
    165//
    166//              // Check saveDirectory is writable
    167//              if( !dir.canWrite() ) {
    168//                      throw new IllegalArgumentException("Not writable: " + saveDirectory);
    169//              }
    170
    171                // Parse the incoming multipart, storing files in the dir provided,
    172                // and populate the meta objects which describe what we found
    173                final MultipartParser parser = new MultipartParser(request, maxPostSize);
    174                if( encoding != null ) {
    175                        parser.setEncoding(encoding);
    176                }
    177
    178//              // 2017/10/06 ADD ｼｽﾃﾑﾘｿｰｽにｸﾗｳﾄﾞｽﾄﾚｰｼﾞ利用が登録されている場合は､ｸﾗｳﾄﾞｽﾄﾚｰｼﾞを利用する
    179//              final String storage = HybsSystem.sys( "CLOUD_TARGET");
    180//              final boolean useStorage = storage != null && storage.length() > 0 ;
    181
    182//              File dir  = null;
    183                // Save the dir
    184                // 5.10.9.0 (2019/03/01) ｸﾗｳﾄﾞｽﾄﾚｰｼﾞ対応 oota tmp
    185                // File dir = new File(saveDirectory);
    186                // 8.0.0.2 (2021/10/15) ﾛｰｶﾙﾌｧｲﾙとｸﾗｳﾄﾞﾌｧｲﾙ間の移動
    187                // 8.0.1.0 (2021/10/29) storageType , bucketName 削除
    188//              final FileOperation dir = HybsFileOperationFactory.create(storageType, bucketName, saveDirectory);
    189//              final FileOperation dir = HybsFileOperationFactory.createDir(storage, bucket, saveDirectory);
    190                final FileOperation dir = HybsFileOperationFactory.createDir(useLocal, saveDirectory);
    191                // 5.10.9.0 (2019/03/01) if条件を追加｡ﾁｪｯｸはﾛｰｶﾙｽﾄﾚｰｼﾞの場合のみ行います｡ oota tmp
    192//              if(dir.isLocal()) {
    193//              if( !dir.isCloud() ) {
    194//                      // ｾｰﾌﾞﾃﾞｨﾚｸﾄﾘ 作成
    195//                      if( ! dir.exists() && ! dir.mkdirs() ) {
    196//                              throw new IllegalArgumentException( "Not make directory: " + saveDirectory );
    197//                      }
    198//
    199//                      // Check saveDirectory is truly a directory
    200//                      if(!dir.isDirectory()) {
    201//                              throw new IllegalArgumentException("Not a directory: " + saveDirectory);
    202//                      }
    203//
    204//                      // Check saveDirectory is writable
    205//                      if(!dir.canWrite()) {
    206//                              throw new IllegalArgumentException("Not writable: " + saveDirectory);
    207//                      }
    208//              }
    209
    210                // 5.7.1.1 (2013/12/13) HTML5 ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞの複数選択（multiple）対応
    211                Part part;
    212                while( (part = parser.readNextPart()) != null ) {
    213                        final String name = part.getName();
    214                        if( part.isParam() && part instanceof ParamPart ) {
    215                                final ParamPart paramPart = (ParamPart)part;
    216                                final String value = paramPart.getStringValue();
    217                                // 6.4.3.1 (2016/02/12) ConcurrentMap 系は､key,val ともに not null 制限です｡
    218                                List<String> existingValues = paramMap.get(name);
    219                                if( existingValues == null ) {
    220                                        existingValues = new ArrayList<>();
    221                                        paramMap.put(name, existingValues);
    222                                }
    223                                existingValues.add(value);
    224                        }
    225                        else if( part.isFile() && part instanceof FilePart ) {
    226                                final FilePart filePart = (FilePart)part;
    227                                final String orgName = filePart.getFilename();          // 5.7.1.1 (2013/12/13) 判りやすいように変数名変更
    228                                if( orgName != null ) {
    229                                        // 5.7.1.1 (2013/12/13) HTML5 ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞの複数選択（multiple）対応
    230                                        // 同一 name で､複数ﾌｧｲﾙを扱う必要があります｡
    231                                        // 3.8.1.2 (2005/12/19) 仮ﾌｧｲﾙでｾｰﾌﾞする｡
    232                                        // 5.6.5.3 (2013/06/28) ｱｯﾌﾟﾛｰﾄﾞ時のﾀﾞﾐｰﾌｧｲﾙ名をもう少しだけﾗﾝﾀﾞﾑにする｡
    233                                        final String uniqKey = RANDOM_KEY + dumyNewFileCnt.getAndIncrement() ;
    234                                        filePart.setFilename( uniqKey );
    235                                        // 標準のﾌｧｲﾙ書き込み 2017/10/06 DELETE ｸﾗｳﾄﾞｽﾄﾚｰｼﾞ利用判定を追加
    236
    237                                        // ﾌｧｲﾙ書き込み
    238                                        // 5.10.9.0 (2019/03/01) ｸﾗｳﾄﾞｽﾄﾚｰｼﾞ対応｡oota tmp
    239                                        // 8.0.1.0 (2021/10/29) storageType , bucketName 削除
    240//                                      filePart.writeTo(dir);
    241//                                      filePart.writeTo(dir, storage, bucket);
    242                                        filePart.writeTo(dir, useLocal);
    243
    244//                                      if( useStorage ){
    245//                                              // ｸﾗｳﾄﾞｽﾄﾚｰｼﾞにｱｯﾌﾟﾛｰﾄﾞ
    246//                                              filePart.writeToCloud( storage, fileURL, request.getSession(true) );
    247//                                      }else{
    248//      //                                      if( dir == null ) { dir = makeDirs( saveDirectory ); }          // 6.9.0.1 (2018/02/05)
    249//                                              // 標準のﾌｧｲﾙ書き込み
    250//                                              filePart.writeTo(dir);
    251//                                      }
    252
    253                                        // 5.7.1.1 (2013/12/13) HTML5 ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞの複数選択（multiple）対応
    254                                        files.add( new UploadedFile(
    255                                                                                        uniqKey,                // 5.7.1.1 (2013/12/13) 順番変更
    256                                                                                        dir.toString(),
    257                                                                                        name,                   // 5.7.1.1 (2013/12/13) 項目追加
    258                                                                                        orgName,
    259                                                                                        filePart.getContentType()));
    260                                }
    261                        }
    262                        else {
    263                                final String errMsg = "Partｵﾌﾞｼﾞｪｸﾄが､ParamPartでもFilePartでもありません｡"
    264                                                        + " class=[" + part.getClass() + "]";
    265                                throw new OgRuntimeException( errMsg );
    266                        }
    267                }
    268
    269                // 5.7.4.3 (2014/03/28) inputFilename は､ﾘｸｴｽﾄ変数が使えるようにします｡
    270                final String filename = getReqParamFileName( inputFilename ) ;
    271
    272                // 3.5.6.5 (2004/08/09) 登録後にﾌｧｲﾙをﾘﾈｰﾑします｡
    273                // 5.7.1.1 (2013/12/13) HTML5 ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞの複数選択（multiple）対応
    274                final int size = files.size();
    275
    276                // 5.7.1.2 (2013/12/20) zip 対応
    277                // 5.9.25.0 (2017/10/06) FileをString型に変更
    278                final String[] tgtFiles = new String[size];
    279                final boolean isZip = filename != null && filename.endsWith( ".zip" ) ;
    280
    281                for( int i=0; i<size; i++ ) {
    282                        final UploadedFile upFile = files.get(i);
    283                        final String name = upFile.getName();           // 5.7.1.1 (2013/12/13)
    284
    285                        String newName = isZip ? null : filename ;
    286                        String prefix = null ;                          // 6.0.2.4 (2014/10/17) _PFX(接頭辞) , _SFX(接尾辞) 機能を追加
    287                        String sufix  = null ;                          // 6.0.2.4 (2014/10/17) _PFX(接頭辞) , _SFX(接尾辞) 機能を追加
    288                        if( newName == null && name != null ) {
    289                                final int adrs = name.lastIndexOf( HybsSystem.JOINT_STRING );   // ｶﾗﾑ__行番号 の __ の位置
    290                                // 6.0.2.4 (2014/10/17) _PFX(接頭辞) , _SFX(接尾辞) 機能を追加
    291                                if( adrs < 0 ) {
    292                                        newName = getParameter( name + "_NEW" );
    293                                        prefix  = getParameter( name + "_PFX" );
    294                                        sufix   = getParameter( name + "_SFX" );
    295                                }
    296                                else {
    297                                        final String name1 = name.substring( 0,adrs );
    298                                        final String name2 = name.substring( adrs );
    299                                        newName = getParameter( name1 + "_NEW" + name2 );
    300                                        prefix  = getParameter( name1 + "_PFX" + name2 );
    301                                        sufix   = getParameter( name1 + "_SFX" + name2 );
    302                                }
    303                        }
    304
    305                        // 5.7.1.1 (2013/12/13) UploadedFile 内で処理するように変更
    306                        // 5.9.25.0 (2017/10/06) MODIFY fileURLとsessionを追加
    307                        // 5.10.9.0 (2019/03/01) ｸﾗｳﾄﾞｽﾄﾚｰｼﾞ対応｡sessionは不要になったため除去｡ ootat tmp
    308                        // 8.0.1.0 (2021/10/29) storageType , bucketName 削除
    309//                      tgtFiles[i] = upFile.renameTo( newName,prefix,sufix,useBackup,fileURL,request.getSession(true) );
    310//                      tgtFiles[i] = upFile.renameTo( newName,prefix,sufix,useBackup,fileURL, storage, bucket);
    311                        tgtFiles[i] = upFile.renameTo( newName,prefix,sufix,useBackup,fileURL, useLocal);
    312                }
    313                // 5.7.1.2 (2013/12/20) zip 対応
    314                // 6.0.0.0 (2014/04/11) 一旦保留にしていましたが､復活します｡
    315//              if( isZip && !useStorage ) {
    316                if( isZip && (useLocal || !dir.isCloud()) ) {
    317                        final File zipFile = new File( saveDirectory,filename );
    318                        // 5.9.25.0 (2017/10/06) tgtFiles が､String型に変更されたため
    319                        final File[] files = new File[tgtFiles.length];
    320                        for( int i=0; i<tgtFiles.length; i++ ) {
    321                                files[i] = new File( tgtFiles[i] );
    322                        }
    323                        ZipArchive.compress( files,zipFile );
    324                }
    325        }
    326
    327        /**
    328         * ﾘｸｴｽﾄﾊﾟﾗﾒｰﾀの名前配列を取得します｡
    329         *
    330         * @return      ﾘｸｴｽﾄﾊﾟﾗﾒｰﾀの名前配列
    331         * @og.rtnNotNull
    332         */
    333        public String[] getParameterNames() {
    334                final Set<String> keyset = paramMap.keySet();
    335                return keyset.toArray( new String[keyset.size()] );
    336        }
    337
    338        /**
    339         * ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞされたﾌｧｲﾙ群のﾌｧｲﾙ配列を取得します｡
    340         *
    341         * @og.rev 5.7.1.1 (2013/12/13) HTML5 ﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞの複数選択（multiple）対応
    342         *
    343         * @return      ｱｯﾌﾟﾛｰﾄﾞされたﾌｧｲﾙ群
    344         * @og.rtnNotNull
    345         */
    346        public UploadedFile[] getUploadedFile() {
    347                return files.toArray( new UploadedFile[files.size()] );
    348        }
    349
    350        /**
    351         * 指定の名前のﾘｸｴｽﾄﾊﾟﾗﾒｰﾀの値を取得します｡
    352         *
    353         * 複数存在する場合は､一番最後の値を返します｡
    354         *
    355         * @param       name    ﾘｸｴｽﾄﾊﾟﾗﾒｰﾀ名
    356         *
    357         * @return      ﾊﾟﾗﾒｰﾀの値
    358         */
    359        public String getParameter( final String name ) {
    360                final List<String> values = paramMap.get(name);
    361
    362                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
    363                return values == null || values.isEmpty() ? null : values.get( values.size() - 1 );
    364        }
    365
    366        /**
    367         * 指定の名前のﾘｸｴｽﾄﾊﾟﾗﾒｰﾀの値を配列型式で取得します｡
    368         *
    369         * @og.rev 5.3.2.0 (2011/02/01) 新規作成
    370         * @og.rev 6.3.9.1 (2015/11/27) null ではなく長さが0の配列を返すことを検討する(findbugs)｡
    371         *
    372         * @param       name    ﾘｸｴｽﾄﾊﾟﾗﾒｰﾀ名
    373         *
    374         * @return      ﾊﾟﾗﾒｰﾀの値配列(存在しない場合は､長さ０の配列を返します)
    375         * @og.rtnNotNull
    376         */
    377        public String[] getParameters( final String name ) {
    378                final List<String> values = paramMap.get(name);
    379                return values == null || values.isEmpty()
    380                                        ? new String[0]
    381                                        : values.toArray( new String[values.size()] );
    382        }
    383
    384        /**
    385         * 指定の名前のﾘｸｴｽﾄﾊﾟﾗﾒｰﾀの値を配列(int)型式で取得します｡
    386         *
    387         * @og.rev 5.3.2.0 (2011/02/01) 新規作成
    388         * @og.rev 5.3.6.0 (2011/06/01) 配列値が""の場合にNumberFormatExceptionが発生するﾊﾞｸﾞを修正
    389         * @og.rev 6.3.9.1 (2015/11/27) null ではなく長さが0の配列を返すことを検討する(findbugs)｡
    390         *
    391         * @param       name    ﾘｸｴｽﾄﾊﾟﾗﾒｰﾀ名
    392         *
    393         * @return      ﾊﾟﾗﾒｰﾀの値配列(存在しない場合は､長さ０の配列を返します)
    394         * @og.rtnNotNull
    395         */
    396        public int[] getIntParameters( final String name ) {
    397                final List<String> values = paramMap.get(name);
    398
    399                return values == null || values.isEmpty()
    400                                        ? new int[0]
    401                                        : values.stream()
    402                                                        .filter( str -> str != null && !str.isEmpty() )         // 条件
    403                                                        .mapToInt( Integer::parseInt )                                          // 変換 String → int
    404                                                        .toArray();                                                                                     // int[] 配列
    405        }
    406
    407        /**
    408         * 指定の名前の ﾌｧｲﾙ名のﾘｸｴｽﾄ変数処理を行います｡
    409         *
    410         * filename 属性のみ､{&#064;XXXX} のﾘｸｴｽﾄ変数が使えるようにします｡
    411         *
    412         * @og.rev 5.7.4.3 (2014/03/28) 新規追加
    413         *
    414         * @param       fname   ﾌｧｲﾙ名
    415         * @return      ﾘｸｴｽﾄ変数を処理したﾌｧｲﾙ名
    416         */
    417        private String getReqParamFileName( final String fname ) {
    418
    419                String rtn = fname ;
    420                if( fname != null ) {
    421                        final StringBuilder filename = new StringBuilder( fname ) ;
    422                        int st = filename.indexOf( "{@" );
    423                        while( st >= 0 ) {
    424                                final int ed = filename.indexOf( "}",st );
    425                                if( ed < 0 ) {
    426                                        final String errMsg = "{@XXXX} の対応関係が取れていません｡"
    427                                                                + " filename=[" + fname + "]";
    428                                        throw new OgRuntimeException( errMsg );
    429                                }
    430                                final String key = filename.substring( st+2,ed );               // "}" は切り出し対象外にする｡
    431                                final String val = getParameter( key );
    432                                filename.replace( st,ed+1,val );                                // "}" を含めて置換したいので､ed+1
    433                                // 次の "{@" を探す｡開始は置換文字数が不明なので､st から始める｡
    434                                st = filename.indexOf( "{@",st );
    435                        }
    436                        rtn = filename.toString();
    437                }
    438                return rtn ;
    439        }
    440
    441//      /**
    442//       * 指定のﾃﾞｨﾚｸﾄﾘが無ければ作成します｡
    443//       *
    444//       * @og.rev 6.9.0.1 (2018/02/05) ﾌｧｲﾙをｾｰﾌﾞするﾃﾞｨﾚｸﾄﾘは､必要な場合のみ､作成します｡
    445//       * @og.rev 8.0.0.0 (2021/09/30) ｸﾗｳﾄﾞ対応のため､
    446//       *
    447//       * @param       saveDir ﾃﾞｨﾚｸﾄﾘ名
    448//       * @return      ｾｰﾌﾞ可能なﾃﾞｨﾚｸﾄﾘ
    449//       * @throws      IllegalArgumentException        ｾｰﾌﾞﾃﾞｨﾚｸﾄﾘ に関係するｴﾗｰ(無理から)
    450//       */
    451//      private File makeDirs( final String saveDir ) throws IllegalArgumentException {
    452//              // ｾｰﾌﾞﾃﾞｨﾚｸﾄﾘの名前ﾁｪｯｸ
    453//              if( saveDir == null ) {
    454//                      throw new IllegalArgumentException( "saveDir cannot be null" );
    455//              }
    456//
    457//              // ｾｰﾌﾞﾃﾞｨﾚｸﾄﾘのｵﾌﾞｼﾞｪｸﾄ
    458//              final File dir = new File( saveDir );
    459//
    460//              // ｾｰﾌﾞﾃﾞｨﾚｸﾄﾘ 作成
    461//              if( ! dir.exists() && ! dir.mkdirs() ) {
    462//                      throw new IllegalArgumentException( "Not make directory: " + saveDir );
    463//              }
    464//
    465//              // ﾃﾞｨﾚｸﾄﾘでなければ､ｴﾗｰ
    466//              if( !dir.isDirectory() ) {
    467//                      throw new IllegalArgumentException( "Not a directory: " + saveDir );
    468//              }
    469//
    470//              // 書込みできなければ､ｴﾗｰ
    471//              if( !dir.canWrite() ) {
    472//                      throw new IllegalArgumentException( "Not writable: " + saveDir );
    473//              }
    474//
    475//              return dir;
    476//      }
    477}