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 */
016 package org.opengion.hayabusa.servlet;
017
018 import java.io.FileInputStream;
019 import java.io.IOException;
020
021 import javax.mail.internet.MimeUtility;
022 import javax.servlet.ServletException;
023 import javax.servlet.ServletOutputStream;
024 import javax.servlet.http.HttpServlet;
025 import javax.servlet.http.HttpServletRequest;
026 import javax.servlet.http.HttpServletResponse;
027
028 import org.opengion.fukurou.security.HybsCryptography;
029 import org.opengion.fukurou.util.Closer;
030 import org.opengion.fukurou.util.KanaFilter;
031 import org.opengion.fukurou.util.StringUtil;
032 import org.opengion.hayabusa.common.HybsSystem;
033 import org.opengion.hayabusa.common.HybsSystemException;
034
035 /**
036 * サーバã?管ç�?ƒ•ァイルをダウンロードするå?合に使用する、サーブレãƒ?ƒˆですã?
037 *
038 * 引数(URL)にæŒ?®šã?ファイルをサーバã?からクライアントにãƒ?‚¦ンロードさせますã?
039 * file には、サーバã?ファイルの物ç�?‚¢ドレスを指定しますã?相対パスを使用する場合ã?ã€?
040 * コンãƒ?‚ストルーãƒ?通常、Tomcatでは、G:\webapps\dbdef2\ など)からのパスと判断しますã?
041 * name には、クライアントに送信するファイル名をæŒ?®šしますã?ファイル名をæŒ?®šしなã�??合ã?ã€?
042 * サーバã?の物ç�?ƒ•ァイルのファイル名が代わりに使用されますã?
043 * 日本語ファイル名ã?、すべて UTF-8化して処ç�?�—ますã?æŒ?®šするファイルに日本語が含まれる
044 * 場合ã?、URLエンコードを行ってくださいã€?
045 * 基本çš?�«はContent-disposition属æ?として"attachment"が指定されますã?
046 * ä½?�—、引数に inline=true を指定することで、Content-disposition属æ?に"inline"が指定されますã?
047 * またã?シスãƒ?ƒ リソースのUSE_FILEDOWNLOAD_CHECKKEYをtrueにæŒ?®šすることで、簡易的なチェãƒ?‚¯ã‚?
048 * 行うことができますã?
049 * 具体的には、これを有効にすると、file属æ?の値から計算されるMD5チェãƒ?‚¯サãƒ?�¨ã€?key"とã�?�†
050 * パラメーターにæŒ?®šされた値がä¸??した場合ã?みãƒ?‚¦ンロードが許可され、keyが指定されてã�?�ªã�??
051 * またã?値が異なるå?合ã?ãƒ?‚¦ンロードエラーとなりますã?
052 *
053 * ä¸?ˆ¬çš?�ªサーブレãƒ?ƒˆと同様に、デプロイメントã?ãƒ?‚£スクリプタ WEB-INF/web.xml にã€?
054 * servlet 要ç´?�¨ そã?マッピング(servlet-mapping)を定義するå¿?¦�がありますã?
055 *
056 * <servlet>
057 * <servlet-name>fileDownload</servlet-name>
058 * <servlet-class>org.opengion.hayabusa.servlet.FileDownload</servlet-class>
059 * </servlet>
060 *
061 * <servlet-mapping>
062 * <servlet-name>fileDownload</servlet-name>
063 * <url-pattern>/jsp/fileDownload</url-pattern>
064 * </servlet-mapping>
065 *
066 * ä¸?ˆ¬には、http://:ポã?ãƒ?シスãƒ?ƒ ID/jsp/fileDownload?file=サーバã?物ç�?ƒ•ァイル&name=ファイルå�?
067 * 形式ã?URL でアクセスしますã?
068 *
069 * forwardでアクセスする場合ã?ファイル名ã?æ–?—コード変換が不要なため、useStrincConvert=falseの
070 * 引数を与えてください。ï¼?alseとしなã�??合ã?日本語ファイル名等でエラーが発生しますï¼?
071 *
072 * @og.rev 3.8.1.1 (2005/11/21) 新規追åŠ?
073 * @og.group そã?他機è?
074 *
075 * @version 0.9.0 2000/10/17
076 * @author Kazuhiko Hasegawa
077 * @since JDK1.1,
078 */
079 public class FileDownload extends HttpServlet {
080 private static final long serialVersionUID = 539020110901L ;
081
082 // 拡張子contentType対応テーブル
083 private static final String CONTENT_TYPE_TABLE[][] = {
084 {"jpg", "image/pjpeg" },
085 {"gif", "image/gif" },
086 {"txt", "text/plain" },
087 // {"xls", "application/vnd.ms-excel"}
088 // OpenDocument追åŠ?
089 {"xls", "application/vnd.ms-excel"},
090 {"odp", "application/vnd.oasis.opendocument.presentation"}, // 4.3.5.5 (2008/03/08)
091 {"ods", "application/vnd.oasis.opendocument.spreadsheet"}, // 4.3.5.5 (2008/03/08)
092 {"odt", "application/vnd.oasis.opendocument.text"} // 4.3.5.5 (2008/03/08)
093 };
094 private static final int EXTENTION = 0;
095 private static final int CONTENT_TYPE= 1;
096
097 /**
098 * GET メソãƒ?ƒ‰が呼ばれたときに実行しますã?
099 *
100 * 処ç�??、doPost へ振りなおしてã�?�¾すã?
101 *
102 * @param request HttpServletRequestオブジェクãƒ?
103 * @param response HttpServletResponseオブジェクãƒ?
104 *
105 * @og.rev 3.8.1.2 (2005/12/19) 半角カãƒ?全角カナ変換機è?の追åŠ?
106 *
107 * @throws ServletException サーブレãƒ?ƒˆ関係ã?エラーが発生したå?合ã?throw されますã?
108 * @throws IOException 入出力エラーが発生したとã�?
109 */
110 @Override
111 public void doGet( final HttpServletRequest request, final HttpServletResponse response )
112 throws ServletException, IOException {
113 doPost( request,response );
114 }
115
116 /**
117 * POST メソãƒ?ƒ‰が呼ばれたときに実行しますã?
118 *
119 * file 引数の サーバã?物ç�?ƒ•ァイルをã?クライアントにストリーãƒ?Œ–して返しますã?
120 * name 引数があれã?、その名前のファイル名でクライアントがファイルセーブできるように
121 * しますã?name 引数がなければ、そのまま物ç�?ƒ•ァイル名が使用されますã?
122 * サーバã?物ç�?ƒ•ァイル名が、相対パスの場合ã?コンãƒ?‚ストルートに対する相対パスになりますã?
123 * (例:G:\webapps\dbdef2\ など)
124 *
125 * @og.rev 5.3.2.0 (2011/02/01) 日本語ファイル名が正しく処ç�?�§きなã�?ƒ�グを修正
126 * @og.rev 5.3.4.0 (2011/04/01) IEでファイルが正しくãƒ?‚¦ンロードできなã�?ƒ�グを修正
127 * @og.rev 5.3.5.0 (2011/05/01) ファイルãƒ?‚¦ンロードチェãƒ?‚¯キー対å¿?
128 * @og.rev 5.3.6.0 (2011/06/01) ファイルãƒ?‚¦ンロードã?attachmentに変更(ãƒ?‚¦ンロードダイアログをå?ã�?
129 * @og.rev 5.3.8.0 (2011/08/01) ファイル名指定でIEの場合ã?URLエンコードすると途中でåˆ?‚Œるたã‚?IE7のバグ)、Shift_JIS(WIndows-31J)で直接æŒ?®šするã?
130 * @og.rev 5.3.9.0 (2011/09/01) 引数にinline=trueを指定することで、インライン表示がå?来るよã�?�«対å¿?
131 * @og.rev 5.7.1.2 (2013/12/20) 日本語ファイルのIE11対応ï¼?A変更??msg â‡?errMsg 変更
132 * @og.rev 5.8.1.0 (2014/11/07) forward時ã?æ–?—コード変換不要対å¿?
133 *
134 * @param request HttpServletRequestオブジェクãƒ?
135 * @param response HttpServletResponseオブジェクãƒ?
136 *
137 * @throws ServletException サーブレãƒ?ƒˆ関係ã?エラーが発生したå?合ã?throw されますã?
138 * @throws IOException 入出力エラーが発生したとã�?
139 */
140 @Override
141 public void doPost( final HttpServletRequest request, final HttpServletResponse response )
142 throws ServletException, IOException {
143
144 // 3.8.1.2 (2005/12/19) 半角カãƒ?全角カナ変換機è?の追åŠ?
145 boolean hanzenFlag = HybsSystem.sysBool( "USE_FILEDOWNLOAD_HAN_ZEN" );
146
147 String reqFilename = request.getParameter( "file" );
148 String newFilename = request.getParameter( "name" );
149
150 // 5.3.9.0 (2011/09/01) 引数にinline=trueを指定することで、インライン表示がå?来るよã�?�«対å¿?
151 boolean inline = StringUtil.nval( request.getParameter( "inline" ), false );
152 String dipositionType = inline ? "inline" : "attachment";
153
154 // 5.8.1.0 (2014/11/07) エンコード変換対å¿?
155 boolean useStrCnv = StringUtil.nval( request.getParameter( "useStringConvert" ), true );
156
157 // クライアントå?のæ–?—エンコーãƒ?‚£ングをUTF-8に変換
158 // 5.8.1.0 (2014/11/07) 条件追åŠ?
159 if( useStrCnv ){
160 reqFilename = new String( reqFilename.getBytes("ISO-8859-1"), "UTF-8" );
161 }
162
163 // 5.3.5.0 (2011/05/01) ファイルãƒ?‚¦ンロードチェãƒ?‚¯キー対å¿?
164 boolean useCheck = HybsSystem.sysBool( "USE_FILEDOWNLOAD_CHECKKEY" );
165 if( useCheck ) {
166 String checkKey = request.getParameter( "key" );
167 if( checkKey == null || !checkKey.equals( HybsCryptography.getMD5( reqFilename ) ) ) {
168 // String msg = "アクセスが拒否されましたã€?URLチェãƒ?‚¯)";
169 // throw new HybsSystemException( msg );
170 String errMsg = "アクセスが拒否されましたã€?URLチェãƒ?‚¯)";
171 throw new HybsSystemException( errMsg ); // 5.7.1.2 (2013/12/20) msg â‡?errMsg 変更
172 }
173 }
174
175 // 相対パスを絶対パスに変換。ファイルセパレータも正規化されてã�?�¾すã?
176 reqFilename = HybsSystem.url2dir( reqFilename );
177
178 // 拡張子からcontentTypeを獲å¾?
179 String contentType = getContentType( reqFilename );
180 // contentTypeをå?åŠ?
181 response.setContentType( contentType );
182
183 // 表示ファイル名ã?æŒ?®?
184 if( newFilename == null || newFilename.length() == 0 ) {
185 newFilename = getFileName( reqFilename );
186 }
187 // else {
188 else if( useStrCnv ){ // 5.8.1.0 (2014/11/07) 条件追åŠ?
189 newFilename = new String( newFilename.getBytes("ISO-8859-1"), "UTF-8" );
190 }
191
192 // 3.8.1.2 (2005/12/19) 半角カナを全角カナに置き換えますã?ファイルãƒ?‚¤アログのæ–?—化け仮対å¿?
193 if( hanzenFlag ) {
194 newFilename = KanaFilter.han2zen( newFilename );
195 }
196
197 // 5.3.2.0 (2011/02/01) 日本語ファイル名が正しく処ç�?�§きなã�?ƒ�グを修正
198 //// newFilename = StringUtil.urlEncode( newFilename );
199 // if( request.getHeader( "User-Agent" ).indexOf( "MSIE" ) == -1 ) {
200 // newFilename = MimeUtility.encodeWord( newFilename, "UTF-8", "B" );
201 // }
202 // else {
203 // // 5.3.8.0 (2011/08/01) IEの場合ã?URLエンコードすると途中でåˆ?‚Œるたã‚?IE7のバグ)、Shift_JIS(WIndows-31J)で直接æŒ?®šするã?
204 //// newFilename = StringUtil.urlEncode( newFilename );
205 // newFilename = new String( newFilename.getBytes("Windows-31J"), "ISO-8859-1" );
206 // }
207
208 // 5.7.1.2 (2013/12/20) 条件を反転させた上でIE11対応を行う
209 if( request.getHeader( "User-Agent" ).indexOf( "MSIE" ) >= 0 || request.getHeader( "User-Agent" ).indexOf( "Trident" ) >= 0 ) {
210 newFilename = new String( newFilename.getBytes("Windows-31J"), "ISO-8859-1" );
211 }
212 else {
213 newFilename = MimeUtility.encodeWord( newFilename, "UTF-8", "B" );
214 }
215
216 // ファイル名ã?送信( attachment部åˆ?‚’inlineに変更すればインライン表示 )
217 // 5.3.9.0 (2011/09/01) 引数にinline=trueを指定することで、インライン表示がå?来るよã�?�«対å¿?
218 // response.setHeader( "Content-disposition", "inline; filename=\"" + newFilename + "\"" );
219 // response.setHeader( "Content-disposition", "attachment; filename=\"" + newFilename + "\"" );
220 response.setHeader( "Content-disposition", dipositionType + "; filename=\"" + newFilename + "\"" );
221
222 // 5.3.4.0 (2011/04/01) IEでファイルが正しくãƒ?‚¦ンロードできなã�?ƒ�グを修正
223 response.setHeader( "Cache-Control", "public" );
224
225 // ファイルå†?®¹の出åŠ?
226 FileInputStream fin = null;
227 ServletOutputStream out = null;
228 try {
229 fin = new FileInputStream( reqFilename );
230 out = response.getOutputStream();
231
232 // ファイル読み込み用バッファ
233 byte buffer[] = new byte[4096];
234 int size;
235 while((size = fin.read(buffer))!=-1) {
236 out.write(buffer,0, size);
237 out.flush();
238 }
239 }
240 finally {
241 Closer.ioClose( fin ); // 4.0.0 (2006/01/31) close 処ç�?™‚の IOException を無è¦?
242 Closer.ioClose( out ); // 4.0.0 (2006/01/31) close 処ç�?™‚の IOException を無è¦?
243 }
244 }
245
246 /**
247 * アドレス名から拡張子を取り出しますã?
248 *
249 * アドレス名ã?後ろからã€?." 以降を拡張子としてåˆ?‚Š取りますã?
250 * 拡張子が存在しなã�??å�?æŒ?®šã?ファイル名に "." が含まれなã�??å�?は
251 * ゼロæ–?—å?("")を返しますã?
252 *
253 * @param fileAddress アドレスå�?
254 *
255 * @return 拡張å?
256 */
257 private String getExtention( final String fileAddress ) {
258 int idx = fileAddress.lastIndexOf('.');
259 if( idx!=-1 ) { return fileAddress.substring( idx+1 ); }
260 return "";
261 }
262
263 /**
264 * アドレス名からファイル名を取り出しますã?
265 *
266 * アドレス名ã?後ろから、ファイルセパレータ以降をファイル名としてåˆ?‚Š取りますã?
267 * ファイルセパレータが存在しなã�??合ã?アドレス名をそã?まま返しますã?
268 * ここでは、OS毎に異なるファイルセパレータを統ä¸?¾Œに処ç�?�—てくださいã€?
269 *
270 * @param fileAddress アドレスå�?
271 *
272 * @return ファイルå�?
273 */
274 private String getFileName( final String fileAddress ) {
275 int idx = fileAddress.lastIndexOf( HybsSystem.FS );
276 if( idx!=-1 ) { return fileAddress.substring( idx+1 ); }
277 return fileAddress;
278 }
279
280 /**
281 * アドレス名から対応するコンãƒ?ƒ³ãƒ?‚¿イプを取り出しますã?
282 *
283 * アドレス名からã?ファイル拡張子を取り出しã?対応するコンãƒ?ƒ³ãƒ?‚¿イプを返しますã?
284 * コンãƒ?ƒ³ãƒ?‚¿イプã?、CONTENT_TYPE_TABLE 配å?に定義してã�?‚‹中から検索して返しますã?
285 * 存在しなã�??合ã?ã€?application/octet-stream" を返しますã?
286 *
287 * @param fileAddress アドレスå�?
288 *
289 * @return コンãƒ?ƒ³ãƒ?‚¿イãƒ?
290 */
291 private String getContentType( final String fileAddress ) {
292 String extention = getExtention( fileAddress );
293 for( int j=0; j<CONTENT_TYPE_TABLE.length; j++ ) {
294 if( CONTENT_TYPE_TABLE[j][EXTENTION].equalsIgnoreCase( extention ) ) {
295 return CONTENT_TYPE_TABLE[j][CONTENT_TYPE];
296 }
297 }
298 return "application/octet-stream";
299 }
300 }