1 /* 2 * $Id: $ 3 * 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 package org.apache.struts.actions; 22 23 import org.apache.struts.action.ActionForm; 24 import org.apache.struts.action.ActionForward; 25 import org.apache.struts.action.ActionMapping; 26 27 import javax.servlet.ServletContext; 28 import javax.servlet.http.HttpServletRequest; 29 import javax.servlet.http.HttpServletResponse; 30 31 import java.io.BufferedInputStream; 32 import java.io.File; 33 import java.io.FileInputStream; 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.io.OutputStream; 37 38 /** 39 * This is an abstract base class that minimizes the amount of special coding 40 * that needs to be written to download a file. All that is required to use 41 * this class is to extend it and implement the <code>getStreamInfo()</code> 42 * method so that it returns the relevant information for the file (or other 43 * stream) to be downloaded. Optionally, the <code>getBufferSize()</code> 44 * method may be overridden to customize the size of the buffer used to 45 * transfer the file. 46 * 47 * @since Struts 1.2.6 48 */ 49 public abstract class DownloadAction extends BaseAction { 50 /** 51 * If the <code>getBufferSize()</code> method is not overridden, this is 52 * the buffer size that will be used to transfer the data to the servlet 53 * output stream. 54 */ 55 protected static final int DEFAULT_BUFFER_SIZE = 4096; 56 57 /** 58 * Returns the information on the file, or other stream, to be downloaded 59 * by this action. This method must be implemented by an extending class. 60 * 61 * @param mapping The ActionMapping used to select this instance. 62 * @param form The optional ActionForm bean for this request (if 63 * any). 64 * @param request The HTTP request we are processing. 65 * @param response The HTTP response we are creating. 66 * @return The information for the file to be downloaded. 67 * @throws Exception if an exception occurs. 68 */ 69 protected abstract StreamInfo getStreamInfo(ActionMapping mapping, 70 ActionForm form, HttpServletRequest request, 71 HttpServletResponse response) 72 throws Exception; 73 74 /** 75 * Returns the size of the buffer to be used in transferring the data to 76 * the servlet output stream. This method may be overridden by an 77 * extending class in order to customize the buffer size. 78 * 79 * @return The size of the transfer buffer, in bytes. 80 */ 81 protected int getBufferSize() { 82 return DEFAULT_BUFFER_SIZE; 83 } 84 85 /** 86 * Process the specified HTTP request, and create the corresponding HTTP 87 * response (or forward to another web component that will create it). 88 * Return an <code>ActionForward</code> instance describing where and how 89 * control should be forwarded, or <code>null</code> if the response has 90 * already been completed. 91 * 92 * @param mapping The ActionMapping used to select this instance. 93 * @param form The optional ActionForm bean for this request (if 94 * any). 95 * @param request The HTTP request we are processing. 96 * @param response The HTTP response we are creating. 97 * @return The forward to which control should be transferred, or 98 * <code>null</code> if the response has been completed. 99 * @throws Exception if an exception occurs. 100 */ 101 public ActionForward execute(ActionMapping mapping, ActionForm form, 102 HttpServletRequest request, HttpServletResponse response) 103 throws Exception { 104 StreamInfo info = getStreamInfo(mapping, form, request, response); 105 String contentType = info.getContentType(); 106 InputStream stream = info.getInputStream(); 107 108 try { 109 response.setContentType(contentType); 110 copy(stream, response.getOutputStream()); 111 } finally { 112 if (stream != null) { 113 stream.close(); 114 } 115 } 116 117 // Tell Struts that we are done with the response. 118 return null; 119 } 120 121 /** 122 * Copy bytes from an <code>InputStream</code> to an 123 * <code>OutputStream</code>. 124 * 125 * @param input The <code>InputStream</code> to read from. 126 * @param output The <code>OutputStream</code> to write to. 127 * @return the number of bytes copied 128 * @throws IOException In case of an I/O problem 129 */ 130 public int copy(InputStream input, OutputStream output) 131 throws IOException { 132 byte[] buffer = new byte[getBufferSize()]; 133 int count = 0; 134 int n = 0; 135 136 while (-1 != (n = input.read(buffer))) { 137 output.write(buffer, 0, n); 138 count += n; 139 } 140 141 return count; 142 } 143 144 /** 145 * The information on a file, or other stream, to be downloaded by the 146 * <code>DownloadAction</code>. 147 */ 148 public static interface StreamInfo { 149 /** 150 * Returns the content type of the stream to be downloaded. 151 * 152 * @return The content type of the stream. 153 */ 154 String getContentType(); 155 156 /** 157 * Returns an input stream on the content to be downloaded. This 158 * stream will be closed by the <code>DownloadAction</code>. 159 * 160 * @return The input stream for the content to be downloaded. 161 * @throws IOException if an error occurs 162 */ 163 InputStream getInputStream() 164 throws IOException; 165 } 166 167 /** 168 * A concrete implementation of the <code>StreamInfo</code> interface 169 * which simplifies the downloading of a file from the disk. 170 */ 171 public static class FileStreamInfo implements StreamInfo { 172 /** 173 * The content type for this stream. 174 */ 175 private String contentType; 176 177 /** 178 * The file to be downloaded. 179 */ 180 private File file; 181 182 /** 183 * Constructs an instance of this class, based on the supplied 184 * parameters. 185 * 186 * @param contentType The content type of the file. 187 * @param file The file to be downloaded. 188 */ 189 public FileStreamInfo(String contentType, File file) { 190 this.contentType = contentType; 191 this.file = file; 192 } 193 194 /** 195 * Returns the content type of the stream to be downloaded. 196 * 197 * @return The content type of the stream. 198 */ 199 public String getContentType() { 200 return this.contentType; 201 } 202 203 /** 204 * Returns an input stream on the file to be downloaded. This stream 205 * will be closed by the <code>DownloadAction</code>. 206 * 207 * @return The input stream for the file to be downloaded. 208 * @throws IOException if an error occurs 209 */ 210 public InputStream getInputStream() 211 throws IOException { 212 FileInputStream fis = new FileInputStream(file); 213 BufferedInputStream bis = new BufferedInputStream(fis); 214 215 return bis; 216 } 217 } 218 219 /** 220 * A concrete implementation of the <code>StreamInfo</code> interface 221 * which simplifies the downloading of a web application resource. 222 */ 223 public static class ResourceStreamInfo implements StreamInfo { 224 /** 225 * The content type for this stream. 226 */ 227 private String contentType; 228 229 /** 230 * The servlet context for the resource to be downloaded. 231 */ 232 private ServletContext context; 233 234 /** 235 * The path to the resource to be downloaded. 236 */ 237 private String path; 238 239 /** 240 * Constructs an instance of this class, based on the supplied 241 * parameters. 242 * 243 * @param contentType The content type of the file. 244 * @param context The servlet context for the resource. 245 * @param path The path to the resource to be downloaded. 246 */ 247 public ResourceStreamInfo(String contentType, ServletContext context, 248 String path) { 249 this.contentType = contentType; 250 this.context = context; 251 this.path = path; 252 } 253 254 /** 255 * Returns the content type of the stream to be downloaded. 256 * 257 * @return The content type of the stream. 258 */ 259 public String getContentType() { 260 return this.contentType; 261 } 262 263 /** 264 * Returns an input stream on the resource to be downloaded. This 265 * stream will be closed by the <code>DownloadAction</code>. 266 * 267 * @return The input stream for the resource to be downloaded. 268 * @throws IOException if an error occurs 269 */ 270 public InputStream getInputStream() 271 throws IOException { 272 return context.getResourceAsStream(path); 273 } 274 } 275 }