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.File;
019
020// import jakarta.servlet.http.HttpSession;                                     // 5.9.25.0 (2017/10/06) クラウドストレージ追加対応
021
022import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
023import static org.opengion.fukurou.system.HybsConst.CR ;        // 6.1.0.0 (2014/12/26)
024import org.opengion.fukurou.util.FileUtil;
025import org.opengion.fukurou.util.FileInfo;                                      // 6.2.0.0 (2015/02/27)
026import org.opengion.fukurou.util.StringUtil;                            // 6.0.2.4 (2014/10/17)
027
028// import org.opengion.hayabusa.common.HybsSystem;                      // 5.9.25.0 (2017/10/06) クラウドストレージ追加対応
029// import org.opengion.hayabusa.io.StorageAPI;                          // 5.9.25.0 (2017/10/06) クラウドストレージ追加対応
030// import org.opengion.hayabusa.io.StorageAPIFactory;           // 5.9.25.0 (2017/10/06) クラウドストレージ追加対応
031import org.opengion.hayabusa.io.HybsFileOperationFactory;       // 8.0.0.0 (2021/09/30)
032
033/**
034 * ファイルをサーバーにアップロードする場合に使用されるファイル管理クラスです。
035 * HTML5 ファイルアップロードの複数選択(multiple)対応 に伴い、一つのクラスとして public化します。
036 *
037 * @og.group その他機能
038 * @og.rev 5.7.1.1 (2013/12/13) HTML5 ファイルアップロードの複数選択(multiple)対応
039 * @og.rev 5.9.25.0 (2017/10/06) クラウドストレージ追加対応
040 *
041 * @version  4.0
042 * @author       Kazuhiko Hasegawa
043 * @since    JDK5.0,
044 */
045public final class UploadedFile implements Comparable<UploadedFile> {
046
047        /** バッファの初期容量を通常より多い目に設定します。  {@value} */
048        public static final int BUFFER_MIDDLE = 200;
049
050        // 5.9.25.0 (2017/10/06) MODIFY File型をString型に変更
051        /** 現時点での置き換え後ファイル名 */
052        private String filename ;
053
054        /** アップロードされたファイル名(ユニークにしておきます) */
055        private final String uniqKey    ;
056        private final String dir                ;
057        private final String name               ;
058        private final String original   ;
059        private final String type               ;
060
061        /**
062         * アップロードファイルの管理オブジェクトを作成します。
063         *
064         * @og.rev 5.7.1.1 (2013/12/13) HTML5 ファイルアップロードの複数選択(multiple)対応
065         *
066         * @param       uniqKey         ユニークキー(初期アップロードファイル名)
067         * @param       dir                     ファイルを保管するフォルダ
068         * @param       name            ファイルアップロードされた時のname属性
069         * @param       original        ファイル名(オリジナル)
070         * @param       type            コンテントタイプ
071         */
072        /* default */ UploadedFile( final String uniqKey, final String dir, final String name, final String original, final String type ) {
073                this.uniqKey    = uniqKey;              // 5.7.1.1 (2013/12/13) uniqKey を確定させる。
074                this.dir                = dir;
075                this.name               = name;
076                this.original   = original;
077                this.type               = type;
078        }
079
080        /**
081         * ファイルアップロードされた時のname属性を取得します。
082         *
083         * @og.rev 5.7.1.1 (2013/12/13) HTML5 ファイルアップロードの複数選択(multiple)対応
084         *
085         * @return      ファイルアップロードされた時のname属性
086         */
087        public String getName() {
088                return name;
089        }
090
091        /**
092         * コンテントタイプを取得します。
093         *
094         * @return      コンテントタイプ
095         */
096        public String getContentType() {
097                return type;
098        }
099
100        /**
101         * ファイル名(置き換え後)を取得します。
102         *
103         * @og.rev 5.7.1.2 (2013/12/20) zip 対応で、Fileオブジェクトを返すようにします。
104         * @og.rev 5.9.25.0 (2017/10/06) FILE型をString型に変更
105         *
106         * @return      ファイル名(置き換え後)
107         */
108        public String getUploadFile(){
109                return filename;
110        }
111
112        /**
113         * Fileオブジェクト(置き換え後)を取得します。
114         *
115         * @og.rev 8.1.2.0 (2022/03/10) Fileオブジェクトを返すようにします。
116         *
117         * @param       useLocal        強制的にローカルファイルを使用する場合、true にセットします。
118         *
119         * @return      Fileオブジェクト(置き換え後)
120         */
121        public File getFile( final boolean useLocal ) {
122                return HybsFileOperationFactory.create( useLocal, dir, filename );
123        }
124
125        /**
126         * ファイル名(置き換え後)の置き換えを実行します。
127         * useBackup = true にすると、dir の直下に、"_backup" フォルダを作成します。
128         * バックアップファイル名は、元のファイル名(拡張子含む) + "_" + 現在時刻のlong値 + "." + 元のファイルの拡張子
129         *
130         * newName が null の場合は、original のファイル名に、変換します。
131         *
132         * 6.0.2.4 (2014/10/17)
133         * useBackup="rename" で、すでに同名のファイルが存在した場合に、"_001" のような文字列を追加したファイルにリネームします。
134         * Windowsの " - コピー (2)" に近いですが、桁数を抑えるのと、useBackup="true" と異なり、過去の同一ファイル名は
135         * そのまま、有効になります。同一ファイルが同一フォルダに存在する場合のみ連番が付与されます。
136         *
137         * newName の指定に、フォルダ名を含めることを可能にしました。
138         *
139         * @og.rev 5.7.1.1 (2013/12/13) 新規追加
140         * @og.rev 5.7.2.1 (2014/01/17) FileUtil.renameTo の引数の順番を間違えていた。
141         * @og.rev 6.0.2.4 (2014/10/17) useBackup 修正、newName に、フォルダ名を含めることが可能
142         * @og.rev 6.2.0.0 (2015/02/27) FileInfoクラスを使用。 (FileUtil#getExtension(String) の廃止)
143         * @og.rev 5.9.25.0 (2017/10/06) returnをString型に変更。引数にfileURLとsessionを追加
144         * @og.rev 5.10.9.0 (2019/03/01) クラウドストレージ対応の追加。
145         * @og.rev 8.0.1.0 (2021/10/29) useLocal 属性を追加。storageType , bucketName 削除
146         *
147         * @param       newName         ファイル名(nullの場合は、オリジナル)
148         * @param       prefix          接頭辞(nullの場合は、何もつけない)
149         * @param       sufix           接尾辞(nullの場合は、何もつけない)
150         * @param       useBackup       置き換え後ファイルの処理方法(true:backupフォルダ/false:しない/rename:重複しない連番)
151         * @param       fileURL         クラウドストレージ用のURL
152//       * @param       hsession        セッション
153//       * @param       storage         クラウドプラグイン名(ローカルファイルを強制する場合は、LOCAL を指定する)
154//       * @param       bucket          バケット名(ローカルファイルを強制する場合は、LOCAL を指定する)
155         * @param       useLocal        強制的にローカルファイルを使用する場合、true にセットします。
156         * @return      最終的に作成されたファイルオブジェクト
157         */
158//      public String renameTo( final String newName , final String prefix , final String sufix , final String useBackup, final String fileURL, final HttpSession hsession ) {
159        public String renameTo( final String newName , final String prefix , final String sufix , final String useBackup, final String fileURL,
160//                              final String storage, final String bucket ) {
161                                final boolean useLocal ) {
162
163                // 6.0.2.4 (2014/10/17) prfix が null でなければ、連結。newName が null なら、original を使う。
164                String newNm = StringUtil.nvalAdd( prefix, StringUtil.coalesce( newName,original ) );
165
166                if( newNm == null || newNm.isEmpty() ) {
167                        final String errMsg = "新ファイル名が存在しません。[" + newNm + "]" ;
168                        throw new OgRuntimeException( errMsg );
169                }
170
171                // 新ファイル名から拡張子取得
172                final String newExt = FileInfo.getSUFIX( newNm );                       // 6.2.3.0 (2015/05/01)
173                if( newExt == null || newExt.isEmpty() ) {                                                              // 拡張子なし
174                        final String oldExt = FileInfo.getSUFIX( original );
175                        newNm = StringUtil.nvalAdd( newNm , sufix , "." , oldExt ) ;            // sufix を入れ込む。
176                }
177                else if( sufix != null ) {                                                                                              // 拡張子あり、sufixあり
178                        final StringBuilder buf = new StringBuilder( newNm );
179                        buf.insert( newNm.length()-newExt.length()-1 , sufix );
180                        newNm = buf.toString() ;
181                }
182
183                // 8.0.1.0 (2021/10/29) storageType , bucketName 削除
184        //      File newFile = new File( dir,newNm );
185//              final File newFile = HybsFileOperationFactory.create(storage, bucket, dir, newNm );     // 8.0.0.0
186                final File newFile = HybsFileOperationFactory.create(useLocal, dir, newNm );    // 8.0.0.0
187
188                // 5.10.9.0 (2019/03/01) MODIFY
189                // File uniqFile = new File( dir , uniqKey );           // 5.7.1.1 (2013/12/13) アップロードされたファイル
190//              final File uniqFile = HybsFileOperationFactory.create(storage, bucket, dir, uniqKey);
191                final File uniqFile = HybsFileOperationFactory.create(useLocal, dir, uniqKey);
192
193                // 5.10.9.0 (2019/03/01) MODIFY HybsFileUtilクラスの利用に変更。
194                FileUtil.renameTo( uniqFile , newFile, useBackup );             // from ⇒ to の順番に指定。
195
196                // (2017/09/22) ADD FILE型をString型に変更したので、getName()をfilenameに設定。
197                filename = newFile.getName();
198
199        //      if( newNm != null && newNm.length() > 0 ) {
200                        // 5.9.25.0 (2017/10/06) ADD bluemixクラウドストレージを利用する処理を追加
201//                      final String storage = HybsSystem.sys( "CLOUD_TARGET");
202//                      if(storage != null && storage.length() > 0){
203//                              // ストレージに保存
204//                              final StorageAPI storageApi = StorageAPIFactory.newStorageAPI(storage, HybsSystem.sys("CLOUD_BUCKET"), hsession);
205//                              storageApi.rename(fileURL, uniqKey, newNm, Boolean.valueOf( useBackup ), hsession);
206//                              // ファイル名をfilenameに設定する
207//                              filename = newNm;
208//                      } else {
209//                              // 標準のファイル作成
210//      //                      newFile = new File( dir,newNm );
211//
212//                              final File uniqFile = new File( dir , uniqKey );                // 5.7.1.1 (2013/12/13) アップロードされたファイル
213//
214//                              // 6.0.2.4 (2014/10/17) 戻り値は、変更後の新しい File オブジェクト
215//                              newFile = FileUtil.renameTo( uniqFile, newFile, useBackup );            // from ⇒ to の順番に指定。
216//
217//                              // 5.7.2.1 (2014/01/17) FileUtil.renameTo の引数の順番を間違えていた。
218//      //                      FileUtil.renameTo( newFile, uniqFile , useBackup );
219//      //                      FileUtil.renameTo( uniqFile , newFile, useBackup );             // from ⇒ to の順番に指定。
220//                              // (2017/09/22) ADD FILE型をString型に変更したので、getName()をfilenameに設定。
221//                              filename = newFile.getName();
222//                      }
223        //      }
224        //      // 5.7.1.1 (2013/12/13) ここの処理が走ることは無いはず。
225        //      else {
226        //              String errMsg = "新ファイル名が存在しません。[" + newNm + "]" ;
227        //              throw new RuntimeException( errMsg );
228        //      }
229
230                        // 5.7.2.1 (2014/01/17) FileUtil.renameTo の引数の順番を間違えていた。
231                        // 6.0.2.4 (2014/10/17) 戻り値は、変更後の新しい File オブジェクト
232                // 新ファイル名のセットは、すべての処理が完了してから、設定する。
233                // 5.9.25.0 (2017/10/06) DELETE FILE型をString型に変更により、前の処理でfinenameは設定済み
234                return filename;
235        }
236
237        /**
238         * ファイル名(オリジナル)を取得します。
239         *
240         * @return      ファイル名(オリジナル)
241         */
242        public String getOriginalFileName() {
243                return original;
244        }
245
246        /**
247         * 自然比較メソッド
248         * インタフェース Comparable の 実装に関連して、再定義しています。
249         * 登録されたシーケンス(画面の表示順)で比較します。
250         * equals メソッドでは、キーの同一性のみに着目して判定しています。
251         * この比較では、(運用上同一キーは発生しませんが)たとえ同一キーが存在した
252         * としても、その比較値が同じになることを保証していません。
253         *
254         * @param   other 比較対象のObject
255         *
256         * @return  このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、等しい場合はゼロ、大きい場合は正の整数
257         * @throws  ClassCastException 引数が UploadedFile ではない場合
258         * @throws  IllegalArgumentException 引数が null の場合
259         */
260        @Override       // Comparable
261        public int compareTo( final UploadedFile other ) {
262                if( other == null ) {
263                        final String errMsg = "引数が、null です。" ;
264                        throw new IllegalArgumentException( errMsg );
265                }
266
267                return uniqKey.compareTo( other.uniqKey );
268        }
269
270        /**
271         * このオブジェクトと他のオブジェクトが等しいかどうかを示します。
272         * 画面は、画面IDが等しければ、言語や表示順に関係なく同一とみなされます。
273         * GUIInfo は、ユーザー個別に扱われ、そのグループには、key は唯一で、かつ
274         * 同一言語内で扱われるオブジェクトの為、同一とみなします。
275         *
276         * @param   object 比較対象の参照オブジェクト
277         *
278         * @return      引数に指定されたオブジェクトとこのオブジェクトが等しい場合は true、そうでない場合は false
279         */
280        @Override
281        public boolean equals( final Object object ) {
282                // 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
283                return object instanceof UploadedFile && uniqKey.equals( ((UploadedFile)object).uniqKey ) ;
284        }
285
286        /**
287         * オブジェクトのハッシュコード値を返します。
288         * このメソッドは、java.util.Hashtable によって提供されるような
289         * ハッシュテーブルで使用するために用意されています。
290         * equals( Object ) メソッドをオーバーライトした場合は、hashCode() メソッドも
291         * 必ず 記述する必要があります。
292         * この実装では、getKey().hashCode() と同値を返します。
293         *
294         * @return  このオブジェクトのハッシュコード値
295         */
296        @Override
297        public int hashCode() {
298                return uniqKey.hashCode() ;
299        }
300
301        /**
302         * オブジェクトの識別子として、詳細な画面情報を返します。
303         *
304         * @return  詳細な画面情報
305         * @og.rtnNotNull
306         */
307        @Override
308        public String toString() {
309                final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE )
310                        .append( this.getClass().getName()                      ).append( CR )
311                        .append( "  uniqKey  :").append( uniqKey        ).append( CR )
312                        .append( "  filename :").append( filename       ).append( CR )
313                        .append( "  name     :").append( name           ).append( CR )
314                        .append( "  dir      :").append( dir            ).append( CR )
315                        .append( "  original :").append( original       ).append( CR )
316                        .append( "  type     :").append( type           ).append( CR );
317                return rtn.toString();
318        }
319}