Index: stripes/src/net/sourceforge/stripes/action/StreamingResolution.java =================================================================== --- stripes/src/net/sourceforge/stripes/action/StreamingResolution.java (revision 935) +++ stripes/src/net/sourceforge/stripes/action/StreamingResolution.java Tue Jun 10 16:34:37 EDT 2008 @@ -24,7 +24,11 @@ import java.io.PrintWriter; import java.io.Reader; import java.io.StringReader; +import java.util.Date; +import java.util.Locale; +import java.text.SimpleDateFormat; + /** *
Resolution for streaming data back to the client (in place of forwarding the user to * another page). Designed to be used for streaming non-page data such as generated images/charts @@ -58,6 +62,8 @@ private String filename; private String contentType; private String characterEncoding; + private long lastModified; + private long length; /** * Constructor only to be used when subclassing the StreamingResolution (usually using @@ -120,6 +126,34 @@ } /** + * Sets the modification-date timestamp. If this property is set, the browser may be able to + * apply it to the downloaded file. If this property is unset, the modification-date parameter + * will be omitted. + * + * @param lastModified The date-time (as a long) that the file was last modified. Optional. + * @return StreamingResolution so that this method call can be chained to the constructor and + * returned. + */ + public StreamingResolution setLastModified(long lastModified) { + this.lastModified = lastModified; + return this; + } + + /** + * Sets the file length. If this property is set, the file size will be reported in the HTTP + * header. This may help with file download progress indicators. If this property is unset, the + * size parameter will be omitted. + * + * @param length The length of the file in bytes. + * @return StreamingResolution so that this method call can be chained to the constructor and + * returned. + */ + public StreamingResolution setLength(long length) { + this.length = length; + return this; + } + + /** * Sets the character encoding that will be set on the request when executing this * resolution. If none is set, then the current character encoding (either the one * selected by the LocalePicker or the container default one) will be used. @@ -132,26 +166,48 @@ /** * Streams data from the InputStream or Reader to the response's OutputStream or PrinterWriter, - * using a moderately sized buffer to ensure that the operation is reasonable efficient. - * Once the InputStream or Reader signaled the end of the stream, close() is called on it. + * using a moderately sized buffer to ensure that the operation is reasonable efficient. Once + * the InputStream or Reader signaled the end of the stream, close() is called on it. * * @param request the HttpServletRequest being processed * @param response the paired HttpServletResponse * @throws IOException if there is a problem accessing one of the streams or reader/writer - * objects used. + * objects used. */ - final public void execute(HttpServletRequest request, HttpServletResponse response) throws Exception { + final public void execute(HttpServletRequest request, HttpServletResponse response) throws + Exception { response.setContentType(this.contentType); - if (this.characterEncoding != null) response.setCharacterEncoding(characterEncoding); + if (this.characterEncoding != null) { + response.setCharacterEncoding(characterEncoding); + } - // If a filename was specified, set the appropriate header - if (this.filename != null) { + // For Content-Disposition spec, see http://www.ietf.org/rfc/rfc2183.txt + if (filename != null || lastModified > 0 || length > 0) { + StringBuilder contentDisposition = new StringBuilder(); + + contentDisposition.append("attachment; "); + + if (filename != null) { - // Value of filename should be RFC 2047 encoded here (see RFC 2616) but few browsers - // support that, so just escape the quote for now - String escaped = this.filename.replace("\"", "\\\""); + // Value of filename should be RFC 2047 encoded here (see RFC 2616) but few browsers + // support that, so just escape the quote for now + String escaped = this.filename.replace("\"", "\\\""); - response.setHeader("Content-Disposition", "attachment; filename=\"" + escaped + "\""); + contentDisposition.append("filename=\"" + escaped + "\"; "); - } + } + if (lastModified > 0) { + SimpleDateFormat rfc822Date = + new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.getDefault()); + Date lastModifiedDate = new Date(lastModified); + + contentDisposition.append("modification-date=\"" + rfc822Date + .format(lastModifiedDate) + "\"; "); + } + if (length > 0) { + contentDisposition.append("size=" + length + "; "); + } + response.setHeader("Content-Disposition", contentDisposition.toString()); + } + stream(response); }