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 * <del>ファイルåã?æŒ?®šã«ã€Content-disposition 属æ?を使用ã—ã¦ã?¾ã™ãŒã€Internet Explorer ã®
046 * 仕様ã§ã€attachment ã§æŒ?®šã™ã‚‹ã¨é–‹ãボタンãŒä½¿ãˆã¾ã›ã‚“ã€?"ファイルå? ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã€?
047 * ã“ã?実è£?§ã¯ã€inline を使用ã™ã‚‹ãŸã‚ã€æ–°ãŸãªç”»é¢ã§é–‹ããŸã„å ´åˆã?ã€ãƒªãƒ³ã‚¯ã«ã‚¿ãƒ¼ã‚²ãƒ?ƒˆå±žæ?ã‚?
048 * æŒ?®šã—ã¦ãã ã•ã„ã€?
049 * http://support.microsoft.com/?scid=kb;ja;436605&spid=2073&sid=269</del>
050 * 基本çš?«ã¯Content-disposition属æ?ã¨ã—ã¦"attachment"ãŒæŒ‡å®šã•れã¾ã™ã?
051 * ä½?—ã€å¼•æ•°ã« inline=true を指定ã™ã‚‹ã“ã¨ã§ã€Content-disposition属æ?ã«"inline"ãŒæŒ‡å®šã•れã¾ã™ã?
052 * ã¾ãŸã?シスãƒ?ƒ リソースã®USE_FILEDOWNLOAD_CHECKKEYã‚’trueã«æŒ?®šã™ã‚‹ã“ã¨ã§ã€ç°¡æ˜“çš„ãªãƒã‚§ãƒ?‚¯ã‚?
053 * 行ã†ã“ã¨ãŒã§ãã¾ã™ã?
054 * 具体的ã«ã¯ã€ã“れを有効ã«ã™ã‚‹ã¨ã€file属æ?ã®å€¤ã‹ã‚‰è¨ˆç®—ã•れるMD5ãƒã‚§ãƒ?‚¯ã‚µãƒ?¨ã€?key"ã¨ã?†
055 * ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã«æŒ?®šã•れãŸå€¤ãŒä¸??ã—ãŸå ´åˆã?ã¿ãƒ?‚¦ãƒ³ãƒãƒ¼ãƒ‰ãŒè¨±å¯ã•れã€keyãŒæŒ‡å®šã•れã¦ã?ªã??
056 * ã¾ãŸã?値ãŒç•°ãªã‚‹å?åˆã?ãƒ?‚¦ãƒ³ãƒãƒ¼ãƒ‰ã‚¨ãƒ©ãƒ¼ã¨ãªã‚Šã¾ã™ã?
057 *
058 * ä¸?ˆ¬çš?ªã‚µãƒ¼ãƒ–レãƒ?ƒˆã¨åŒæ§˜ã«ã€ãƒ‡ãƒ—ãƒã‚¤ãƒ¡ãƒ³ãƒˆã?ãƒ?‚£ã‚¹ã‚¯ãƒªãƒ—ã‚¿ WEB-INF/web.xml ã«ã€?
059 * servlet è¦ç´?¨ ãã?マッピング(servlet-mapping)を定義ã™ã‚‹å¿?¦ãŒã‚りã¾ã™ã?
060 *
061 * <servlet>
062 * <servlet-name>fileDownload</servlet-name>
063 * <servlet-class>org.opengion.hayabusa.servlet.FileDownload</servlet-class>
064 * </servlet>
065 *
066 * <servlet-mapping>
067 * <servlet-name>fileDownload</servlet-name>
068 * <url-pattern>/jsp/fileDownload</url-pattern>
069 * </servlet-mapping>
070 *
071 * ä¸?ˆ¬ã«ã¯ã€http://:ãƒã?ãƒ?シスãƒ?ƒ ID/jsp/fileDownload?file=サーãƒã?物ç?ƒ•ァイル&name=ファイルå?
072 * å½¢å¼ã?URL ã§ã‚¢ã‚¯ã‚»ã‚¹ã—ã¾ã™ã?
073 *
074 * @og.rev 3.8.1.1 (2005/11/21) æ–°è¦è¿½åŠ?
075 * @og.group ãã?他機è?
076 *
077 * @version 0.9.0 2000/10/17
078 * @author Kazuhiko Hasegawa
079 * @since JDK1.1,
080 */
081 public class FileDownload extends HttpServlet {
082 private static final long serialVersionUID = 539020110901L ;
083
084 // æ‹¡å¼µåcontentType対応テーブル
085 private static final String CONTENT_TYPE_TABLE[][] = {
086 {"jpg", "image/pjpeg" },
087 {"gif", "image/gif" },
088 {"txt", "text/plain" },
089 // {"xls", "application/vnd.ms-excel"}
090 // OpenDocument追�
091 {"xls", "application/vnd.ms-excel"},
092 {"odp", "application/vnd.oasis.opendocument.presentation"}, // 4.3.5.5 (2008/03/08)
093 {"ods", "application/vnd.oasis.opendocument.spreadsheet"}, // 4.3.5.5 (2008/03/08)
094 {"odt", "application/vnd.oasis.opendocument.text"} // 4.3.5.5 (2008/03/08)
095 };
096 private static final int EXTENTION = 0;
097 private static final int CONTENT_TYPE= 1;
098
099 /**
100 * GET メソãƒ?ƒ‰ãŒå‘¼ã°ã‚ŒãŸã¨ãã«å®Ÿè¡Œã—ã¾ã™ã?
101 *
102 * 処ç??ã€doPost ã¸æŒ¯ã‚ŠãªãŠã—ã¦ã?¾ã™ã?
103 *
104 * @param request HttpServletRequestオブジェク�
105 * @param response HttpServletResponseオブジェク�
106 *
107 * @og.rev 3.8.1.2 (2005/12/19) åŠè§’ã‚«ãƒ?å…¨è§’ã‚«ãƒŠå¤‰æ›æ©Ÿè?ã®è¿½åŠ?
108 *
109 * @throws ServletException
110 * @throws IOException
111 */
112 @Override
113 public void doGet( final HttpServletRequest request, final HttpServletResponse response )
114 throws ServletException, IOException {
115 doPost( request,response );
116 }
117
118 /**
119 * POST メソãƒ?ƒ‰ãŒå‘¼ã°ã‚ŒãŸã¨ãã«å®Ÿè¡Œã—ã¾ã™ã?
120 *
121 * file 引数㮠サーãƒã?物ç?ƒ•ァイルをã?クライアントã«ã‚¹ãƒˆãƒªãƒ¼ãƒ?Œ–ã—ã¦è¿”ã—ã¾ã™ã?
122 * name 引数ãŒã‚れã?ã€ãã®åå‰ã®ãƒ•ァイルåã§ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆãŒãƒ•ァイルセーブã§ãるよã†ã«
123 * ã—ã¾ã™ã?name 引数ãŒãªã‘れã°ã€ãã®ã¾ã¾ç‰©ç?ƒ•ァイルåãŒä½¿ç”¨ã•れã¾ã™ã?
124 * サーãƒã?物ç?ƒ•ァイルåãŒã€ç›¸å¯¾ãƒ‘スã®å ´åˆã?コンãƒ?‚ストルートã«å¯¾ã™ã‚‹ç›¸å¯¾ãƒ‘スã«ãªã‚Šã¾ã™ã?
125 * (例:G:\webapps\dbdef2\ ãªã©)
126 *
127 * @og.rev 5.3.2.0 (2011/02/01) 日本語ファイルåãŒæ£ã—ã処ç?§ããªã?ƒã‚°ã‚’ä¿®æ£
128 * @og.rev 5.3.4.0 (2011/04/01) IEã§ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ£ã—ããƒ?‚¦ãƒ³ãƒãƒ¼ãƒ‰ã§ããªã?ƒã‚°ã‚’ä¿®æ£
129 * @og.rev 5.3.5.0 (2011/05/01) ファイルãƒ?‚¦ãƒ³ãƒãƒ¼ãƒ‰ãƒã‚§ãƒ?‚¯ã‚ー対å¿?
130 * @og.rev 5.3.6.0 (2011/06/01) ファイルãƒ?‚¦ãƒ³ãƒãƒ¼ãƒ‰ã?attachmentã«å¤‰æ›´(ãƒ?‚¦ãƒ³ãƒãƒ¼ãƒ‰ãƒ€ã‚¤ã‚¢ãƒã‚°ã‚’å?ã?
131 * @og.rev 5.3.8.0 (2011/08/01) ãƒ•ã‚¡ã‚¤ãƒ«åæŒ‡å®šã§IEã®å ´åˆã?URLエンコードã™ã‚‹ã¨é€”ä¸ã§åˆ?‚Œã‚‹ãŸã‚?IE7ã®ãƒã‚°)ã€Shift_JIS(WIndows-31J)ã§ç›´æŽ¥æŒ?®šã™ã‚‹ã?
132 * @og.rev 5.3.9.0 (2011/09/01) 引数ã«inline=trueを指定ã™ã‚‹ã“ã¨ã§ã€ã‚¤ãƒ³ãƒ©ã‚¤ãƒ³è¡¨ç¤ºãŒå?æ¥ã‚‹ã‚ˆã?«å¯¾å¿?
133 *
134 * @param request HttpServletRequestオブジェク�
135 * @param response HttpServletResponseオブジェク�
136 *
137 * @throws ServletException
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 // クライアントå?ã®æ–?—エンコーãƒ?‚£ãƒ³ã‚°ã‚’UTF-8ã«å¤‰æ›
155 reqFilename = new String( reqFilename.getBytes("ISO-8859-1"), "UTF-8" );
156
157 // 5.3.5.0 (2011/05/01) ファイルãƒ?‚¦ãƒ³ãƒãƒ¼ãƒ‰ãƒã‚§ãƒ?‚¯ã‚ー対å¿?
158 boolean useCheck = HybsSystem.sysBool( "USE_FILEDOWNLOAD_CHECKKEY" );
159 if( useCheck ) {
160 String checkKey = request.getParameter( "key" );
161 if( checkKey == null || !checkKey.equals( HybsCryptography.getMD5( reqFilename ) ) ) {
162 String msg = "ã‚¢ã‚¯ã‚»ã‚¹ãŒæ‹’å¦ã•れã¾ã—ãŸã€?URLãƒã‚§ãƒ?‚¯)";
163 throw new HybsSystemException( msg );
164 }
165 }
166
167 // 相対パスを絶対パスã«å¤‰æ›ã€‚ファイルセパレータもæ£è¦åŒ–ã•れã¦ã?¾ã™ã?
168 reqFilename = HybsSystem.url2dir( reqFilename );
169
170 // æ‹¡å¼µåã‹ã‚‰contentTypeã‚’ç²å¾?
171 String contentType = getContentType( reqFilename );
172 // contentTypeã‚’å?åŠ?
173 response.setContentType( contentType );
174
175 // 表示ファイルåã?æŒ?®?
176 if( newFilename == null || newFilename.length() == 0 ) {
177 newFilename = getFileName( reqFilename );
178 }
179 else {
180 newFilename = new String( newFilename.getBytes("ISO-8859-1"), "UTF-8" );
181 }
182
183 // 3.8.1.2 (2005/12/19) åŠè§’カナを全角カナã«ç½®ãæ›ãˆã¾ã™ã?ファイルãƒ?‚¤ã‚¢ãƒã‚°ã®æ–?—化ã‘仮対å¿?
184 if( hanzenFlag ) {
185 newFilename = KanaFilter.han2zen( newFilename );
186 }
187
188 // 5.3.2.0 (2011/02/01) 日本語ファイルåãŒæ£ã—ã処ç?§ããªã?ƒã‚°ã‚’ä¿®æ£
189 // newFilename = StringUtil.urlEncode( newFilename );
190 if( request.getHeader( "User-Agent" ).indexOf( "MSIE" ) == -1 ) {
191 newFilename = MimeUtility.encodeWord( newFilename, "UTF-8", "B" );
192 }
193 else {
194 // 5.3.8.0 (2011/08/01) IEã®å ´åˆã?URLエンコードã™ã‚‹ã¨é€”ä¸ã§åˆ?‚Œã‚‹ãŸã‚?IE7ã®ãƒã‚°)ã€Shift_JIS(WIndows-31J)ã§ç›´æŽ¥æŒ?®šã™ã‚‹ã?
195 // newFilename = StringUtil.urlEncode( newFilename );
196 newFilename = new String( newFilename.getBytes("Windows-31J"), "ISO-8859-1" );
197 }
198
199 // ファイルåã?é€ä¿¡( attachment部åˆ?‚’inlineã«å¤‰æ›´ã™ã‚Œã°ã‚¤ãƒ³ãƒ©ã‚¤ãƒ³è¡¨ç¤º )
200 // 5.3.9.0 (2011/09/01) 引数ã«inline=trueを指定ã™ã‚‹ã“ã¨ã§ã€ã‚¤ãƒ³ãƒ©ã‚¤ãƒ³è¡¨ç¤ºãŒå?æ¥ã‚‹ã‚ˆã?«å¯¾å¿?
201 // response.setHeader( "Content-disposition", "inline; filename=\"" + newFilename + "\"" );
202 // response.setHeader( "Content-disposition", "attachment; filename=\"" + newFilename + "\"" );
203 response.setHeader( "Content-disposition", dipositionType + "; filename=\"" + newFilename + "\"" );
204
205 // 5.3.4.0 (2011/04/01) IEã§ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ£ã—ããƒ?‚¦ãƒ³ãƒãƒ¼ãƒ‰ã§ããªã?ƒã‚°ã‚’ä¿®æ£
206 response.setHeader( "Cache-Control", "public" );
207
208 // ファイルå†?®¹ã®å‡ºåŠ?
209 FileInputStream fin = null;
210 ServletOutputStream out = null;
211 try {
212 fin = new FileInputStream( reqFilename );
213 out = response.getOutputStream();
214
215 // ファイルèªã¿è¾¼ã¿ç”¨ãƒãƒƒãƒ•ã‚¡
216 byte buffer[] = new byte[4096];
217 int size;
218 while((size = fin.read(buffer))!=-1) {
219 out.write(buffer,0, size);
220 out.flush();
221 }
222 }
223 finally {
224 Closer.ioClose( fin ); // 4.0.0 (2006/01/31) close 処ç?™‚ã® IOException ã‚’ç„¡è¦?
225 Closer.ioClose( out ); // 4.0.0 (2006/01/31) close 処ç?™‚ã® IOException ã‚’ç„¡è¦?
226 }
227 }
228
229 /**
230 * アドレスåã‹ã‚‰æ‹¡å¼µåã‚’å–り出ã—ã¾ã™ã?
231 *
232 * アドレスåã?後ã‚ã‹ã‚‰ã€?." 以é™ã‚’æ‹¡å¼µåã¨ã—ã¦åˆ?‚Šå–りã¾ã™ã?
233 * æ‹¡å¼µåãŒå˜åœ¨ã—ãªã??å?æŒ?®šã?ファイルåã« "." ãŒå«ã¾ã‚Œãªã??å?ã¯
234 * ã‚¼ãƒæ–?—å?("")ã‚’è¿”ã—ã¾ã™ã?
235 *
236 * @param fileAddress アドレスå?
237 *
238 * @return æ‹¡å¼µå?
239 */
240 private String getExtention( final String fileAddress ) {
241 int idx = fileAddress.lastIndexOf('.');
242 if( idx!=-1 ) { return fileAddress.substring( idx+1 ); }
243 return "";
244 }
245
246 /**
247 * アドレスåã‹ã‚‰ãƒ•ァイルåã‚’å–り出ã—ã¾ã™ã?
248 *
249 * アドレスåã?後ã‚ã‹ã‚‰ã€ãƒ•ァイルセパレータ以é™ã‚’ファイルåã¨ã—ã¦åˆ?‚Šå–りã¾ã™ã?
250 * ファイルセパレータãŒå˜åœ¨ã—ãªã??åˆã?アドレスåã‚’ãã?ã¾ã¾è¿”ã—ã¾ã™ã?
251 * ã“ã“ã§ã¯ã€OS毎ã«ç•°ãªã‚‹ãƒ•ァイルセパレータを統ä¸?¾Œã«å‡¦ç?—ã¦ãã ã•ã„ã€?
252 *
253 * @param fileAddress アドレスå?
254 *
255 * @return ファイルå?
256 */
257 private String getFileName( final String fileAddress ) {
258 int idx = fileAddress.lastIndexOf( HybsSystem.FS );
259 if( idx!=-1 ) { return fileAddress.substring( idx+1 ); }
260 return fileAddress;
261 }
262
263 /**
264 * アドレスåã‹ã‚‰å¯¾å¿œã™ã‚‹ã‚³ãƒ³ãƒ?ƒ³ãƒ?‚¿ã‚¤ãƒ—ã‚’å–り出ã—ã¾ã™ã?
265 *
266 * アドレスåã‹ã‚‰ã?ファイル拡張åã‚’å–り出ã—ã?対応ã™ã‚‹ã‚³ãƒ³ãƒ?ƒ³ãƒ?‚¿ã‚¤ãƒ—ã‚’è¿”ã—ã¾ã™ã?
267 * コンãƒ?ƒ³ãƒ?‚¿ã‚¤ãƒ—ã?ã€CONTENT_TYPE_TABLE é…å?ã«å®šç¾©ã—ã¦ã?‚‹ä¸ã‹ã‚‰æ¤œç´¢ã—ã¦è¿”ã—ã¾ã™ã?
268 * å˜åœ¨ã—ãªã??åˆã?ã€?application/octet-stream" ã‚’è¿”ã—ã¾ã™ã?
269 *
270 * @param fileAddress アドレスå?
271 *
272 * @return コンãƒ?ƒ³ãƒ?‚¿ã‚¤ãƒ?
273 */
274 private String getContentType( final String fileAddress ) {
275 String extention = getExtention( fileAddress );
276 for( int j=0; j<CONTENT_TYPE_TABLE.length; j++ ) {
277 if( CONTENT_TYPE_TABLE[j][EXTENTION].equalsIgnoreCase( extention ) ) {
278 return CONTENT_TYPE_TABLE[j][CONTENT_TYPE];
279 }
280 }
281 return "application/octet-stream";
282 }
283 }