diff options
Diffstat (limited to 'server/src/com/vaadin/terminal/DownloadStream.java')
-rw-r--r-- | server/src/com/vaadin/terminal/DownloadStream.java | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/server/src/com/vaadin/terminal/DownloadStream.java b/server/src/com/vaadin/terminal/DownloadStream.java new file mode 100644 index 0000000000..30810d5da4 --- /dev/null +++ b/server/src/com/vaadin/terminal/DownloadStream.java @@ -0,0 +1,345 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.terminal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import com.vaadin.terminal.gwt.server.Constants; + +/** + * Downloadable stream. + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings("serial") +public class DownloadStream implements Serializable { + + /** + * Maximum cache time. + */ + public static final long MAX_CACHETIME = Long.MAX_VALUE; + + /** + * Default cache time. + */ + public static final long DEFAULT_CACHETIME = 1000 * 60 * 60 * 24; + + private InputStream stream; + + private String contentType; + + private String fileName; + + private Map<String, String> params; + + private long cacheTime = DEFAULT_CACHETIME; + + private int bufferSize = 0; + + /** + * Creates a new instance of DownloadStream. + */ + public DownloadStream(InputStream stream, String contentType, + String fileName) { + setStream(stream); + setContentType(contentType); + setFileName(fileName); + } + + /** + * Gets downloadable stream. + * + * @return output stream. + */ + public InputStream getStream() { + return stream; + } + + /** + * Sets the stream. + * + * @param stream + * The stream to set + */ + public void setStream(InputStream stream) { + this.stream = stream; + } + + /** + * Gets stream content type. + * + * @return type of the stream content. + */ + public String getContentType() { + return contentType; + } + + /** + * Sets stream content type. + * + * @param contentType + * the contentType to set + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + /** + * Returns the file name. + * + * @return the name of the file. + */ + public String getFileName() { + return fileName; + } + + /** + * Sets the file name. + * + * @param fileName + * the file name to set. + */ + public void setFileName(String fileName) { + this.fileName = fileName; + } + + /** + * Sets a paramater for download stream. Parameters are optional information + * about the downloadable stream and their meaning depends on the used + * adapter. For example in WebAdapter they are interpreted as HTTP response + * headers. + * + * If the parameters by this name exists, the old value is replaced. + * + * @param name + * the Name of the parameter to set. + * @param value + * the Value of the parameter to set. + */ + public void setParameter(String name, String value) { + if (params == null) { + params = new HashMap<String, String>(); + } + params.put(name, value); + } + + /** + * Gets a paramater for download stream. Parameters are optional information + * about the downloadable stream and their meaning depends on the used + * adapter. For example in WebAdapter they are interpreted as HTTP response + * headers. + * + * @param name + * the Name of the parameter to set. + * @return Value of the parameter or null if the parameter does not exist. + */ + public String getParameter(String name) { + if (params != null) { + return params.get(name); + } + return null; + } + + /** + * Gets the names of the parameters. + * + * @return Iterator of names or null if no parameters are set. + */ + public Iterator<String> getParameterNames() { + if (params != null) { + return params.keySet().iterator(); + } + return null; + } + + /** + * Gets length of cache expiration time. This gives the adapter the + * possibility cache streams sent to the client. The caching may be made in + * adapter or at the client if the client supports caching. Default is + * <code>DEFAULT_CACHETIME</code>. + * + * @return Cache time in milliseconds + */ + public long getCacheTime() { + return cacheTime; + } + + /** + * Sets length of cache expiration time. This gives the adapter the + * possibility cache streams sent to the client. The caching may be made in + * adapter or at the client if the client supports caching. Zero or negavive + * value disbales the caching of this stream. + * + * @param cacheTime + * the cache time in milliseconds. + */ + public void setCacheTime(long cacheTime) { + this.cacheTime = cacheTime; + } + + /** + * Gets the size of the download buffer. + * + * @return int The size of the buffer in bytes. + */ + public int getBufferSize() { + return bufferSize; + } + + /** + * Sets the size of the download buffer. + * + * @param bufferSize + * the size of the buffer in bytes. + * + * @since 7.0 + */ + public void setBufferSize(int bufferSize) { + this.bufferSize = bufferSize; + } + + /** + * Writes this download stream to a wrapped response. This takes care of + * setting response headers according to what is defined in this download + * stream ({@link #getContentType()}, {@link #getCacheTime()}, + * {@link #getFileName()}) and transferring the data from the stream ( + * {@link #getStream()}) to the response. Defined parameters ( + * {@link #getParameterNames()}) are also included as headers in the + * response. If there's is a parameter named <code>Location</code>, a + * redirect (302 Moved temporarily) is sent instead of the contents of this + * stream. + * + * @param response + * the wrapped response to write this download stream to + * @throws IOException + * passed through from the wrapped response + * + * @since 7.0 + */ + public void writeTo(WrappedResponse response) throws IOException { + if (getParameter("Location") != null) { + response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + response.setHeader("Location", getParameter("Location")); + return; + } + + // Download from given stream + final InputStream data = getStream(); + if (data != null) { + + OutputStream out = null; + try { + // Sets content type + response.setContentType(getContentType()); + + // Sets cache headers + response.setCacheTime(getCacheTime()); + + // Copy download stream parameters directly + // to HTTP headers. + final Iterator<String> i = getParameterNames(); + if (i != null) { + while (i.hasNext()) { + final String param = i.next(); + response.setHeader(param, getParameter(param)); + } + } + + // suggest local filename from DownloadStream if + // Content-Disposition + // not explicitly set + String contentDispositionValue = getParameter("Content-Disposition"); + if (contentDispositionValue == null) { + contentDispositionValue = "filename=\"" + getFileName() + + "\""; + response.setHeader("Content-Disposition", + contentDispositionValue); + } + + int bufferSize = getBufferSize(); + if (bufferSize <= 0 || bufferSize > Constants.MAX_BUFFER_SIZE) { + bufferSize = Constants.DEFAULT_BUFFER_SIZE; + } + final byte[] buffer = new byte[bufferSize]; + int bytesRead = 0; + + out = response.getOutputStream(); + + long totalWritten = 0; + while ((bytesRead = data.read(buffer)) > 0) { + out.write(buffer, 0, bytesRead); + + totalWritten += bytesRead; + if (totalWritten >= buffer.length) { + // Avoid chunked encoding for small resources + out.flush(); + } + } + } finally { + tryToCloseStream(out); + tryToCloseStream(data); + } + } + } + + /** + * Helper method that tries to close an output stream and ignores any + * exceptions. + * + * @param out + * the output stream to close, <code>null</code> is also + * supported + */ + static void tryToCloseStream(OutputStream out) { + try { + // try to close output stream (e.g. file handle) + if (out != null) { + out.close(); + } + } catch (IOException e1) { + // NOP + } + } + + /** + * Helper method that tries to close an input stream and ignores any + * exceptions. + * + * @param in + * the input stream to close, <code>null</code> is also supported + */ + static void tryToCloseStream(InputStream in) { + try { + // try to close output stream (e.g. file handle) + if (in != null) { + in.close(); + } + } catch (IOException e1) { + // NOP + } + } + +} |