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