123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- /*
- * Copyright 2000-2014 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.server;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.Serializable;
- import java.io.UnsupportedEncodingException;
- import java.net.URLEncoder;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
-
- import javax.servlet.http.HttpServletResponse;
-
- /**
- * Downloadable stream.
- * <p>
- * Note that the methods in a DownloadStream are called without locking the
- * session to prevent locking the session during long file downloads. If your
- * DownloadStream uses anything from the session, you must handle the locking.
- * </p>
- *
- * @author Vaadin Ltd.
- * @since 3.0
- */
- @SuppressWarnings("serial")
- public class DownloadStream implements Serializable {
-
- public static final String CONTENT_DISPOSITION = "Content-Disposition";
-
- /**
- * 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 Vaadin 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 request
- * the request for which the response should be written
- * @param response
- * the Vaadin response to write this download stream to
- *
- * @throws IOException
- * passed through from the Vaadin response
- *
- * @since 7.0
- */
- public void writeResponse(VaadinRequest request, VaadinResponse 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) {
- response.setStatus(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
-
- 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));
- }
- }
-
- // Content-Disposition: attachment generally forces download
- String contentDisposition = getParameter(CONTENT_DISPOSITION);
- if (contentDisposition == null) {
- contentDisposition = getContentDispositionFilename(getFileName());
- }
-
- response.setHeader(CONTENT_DISPOSITION, contentDisposition);
-
- 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);
- }
- }
- }
-
- /**
- * Returns the filename formatted for inclusion in a Content-Disposition
- * header. Includes both a plain version of the name and a UTF-8 version
- *
- * @since 7.4.8
- * @param filename
- * The filename to include
- * @return A value for inclusion in a Content-Disposition header
- */
- public static String getContentDispositionFilename(String filename) {
- try {
- String encodedFilename = URLEncoder.encode(filename, "UTF-8");
- return String.format("filename=\"%s\"; filename*=utf-8''%s",
- encodedFilename, encodedFilename);
- } catch (UnsupportedEncodingException e) {
- return null;
- }
- }
-
- /**
- * 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
- }
- }
-
- }
|