You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FileDownloader.java 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.server;
  17. import java.io.IOException;
  18. import com.vaadin.shared.extension.filedownloader.FileDownloaderState;
  19. import com.vaadin.ui.AbstractComponent;
  20. /**
  21. * Extension that starts a download when the extended component is clicked. This
  22. * is used to overcome two challenges:
  23. * <ul>
  24. * <li>Resource should be bound to a component to allow it to be garbage
  25. * collected when there are no longer any ways of reaching the resource.</li>
  26. * <li>Download should be started directly when the user clicks e.g. a Button
  27. * without going through a server-side click listener to avoid triggering
  28. * security warnings in some browsers.</li>
  29. * </ul>
  30. * <p>
  31. * Please note that the download will be started in an iframe, which means that
  32. * care should be taken to avoid serving content types that might make the
  33. * browser attempt to show the content using a plugin instead of downloading it.
  34. * Connector resources (e.g. {@link FileResource} and {@link ClassResource})
  35. * will automatically be served using a
  36. * <code>Content-Type: application/octet-stream</code> header unless
  37. * {@link #setOverrideContentType(boolean)} has been set to <code>false</code>
  38. * while files served in other ways, (e.g. {@link ExternalResource} or
  39. * {@link ThemeResource}) will not automatically get this treatment.
  40. * </p>
  41. *
  42. * @author Vaadin Ltd
  43. * @since 7.0.0
  44. */
  45. public class FileDownloader extends AbstractExtension {
  46. private boolean overrideContentType = true;
  47. /**
  48. * Creates a new file downloader for the given resource. To use the
  49. * downloader, you should also {@link #extend(AbstractClientConnector)} the
  50. * component.
  51. *
  52. * @param resource
  53. * the resource to download when the user clicks the extended
  54. * component.
  55. */
  56. public FileDownloader(Resource resource) {
  57. if (resource == null) {
  58. throw new IllegalArgumentException("resource may not be null");
  59. }
  60. setResource("dl", resource);
  61. }
  62. /**
  63. * Add this extension to the target component.
  64. *
  65. * @param target
  66. * the component to attach this extension to
  67. */
  68. public void extend(AbstractComponent target) {
  69. super.extend(target);
  70. }
  71. /**
  72. * Add this extension to the {@code EventTrigger}.
  73. *
  74. * @param eventTrigger
  75. * the trigger to attach this extension to
  76. * @since 8.4
  77. */
  78. public void extend(EventTrigger eventTrigger) {
  79. super.extend(eventTrigger.getConnector());
  80. getState().partInformation = eventTrigger.getPartInformation();
  81. }
  82. /**
  83. * Gets the resource set for download.
  84. *
  85. * @return the resource that will be downloaded if clicking the extended
  86. * component
  87. */
  88. public Resource getFileDownloadResource() {
  89. return getResource("dl");
  90. }
  91. /**
  92. * Sets the resource that is downloaded when the extended component is
  93. * clicked.
  94. *
  95. * @param resource
  96. * the resource to download
  97. */
  98. public void setFileDownloadResource(Resource resource) {
  99. setResource("dl", resource);
  100. }
  101. /**
  102. * Sets whether the content type of served resources should be overridden to
  103. * <code>application/octet-stream</code> to reduce the risk of a browser
  104. * plugin choosing to display the resource instead of downloading it. This
  105. * is by default set to <code>true</code>.
  106. * <p>
  107. * Please note that this only affects Connector resources (e.g.
  108. * {@link FileResource} and {@link ClassResource}) but not other resource
  109. * types (e.g. {@link ExternalResource} or {@link ThemeResource}).
  110. * </p>
  111. *
  112. * @param overrideContentType
  113. * <code>true</code> to override the content type if possible;
  114. * <code>false</code> to use the original content type.
  115. */
  116. public void setOverrideContentType(boolean overrideContentType) {
  117. this.overrideContentType = overrideContentType;
  118. }
  119. /**
  120. * Checks whether the content type should be overridden.
  121. *
  122. * @return <code>true</code> if the content type will be overridden when
  123. * possible; <code>false</code> if the original content type will be
  124. * used.
  125. * @see #setOverrideContentType(boolean)
  126. */
  127. public boolean isOverrideContentType() {
  128. return overrideContentType;
  129. }
  130. /**
  131. * {@inheritDoc}
  132. *
  133. * @throws IOException
  134. * if something goes wrong with the download or the user
  135. * cancelled the file download process.
  136. */
  137. @Override
  138. public boolean handleConnectorRequest(VaadinRequest request,
  139. VaadinResponse response, String path) throws IOException {
  140. if (!path.matches("dl(/.*)?")) {
  141. // Ignore if it isn't for us
  142. return false;
  143. }
  144. VaadinSession session = getSession();
  145. session.lock();
  146. DownloadStream stream;
  147. try {
  148. Resource resource = getFileDownloadResource();
  149. if (!(resource instanceof ConnectorResource)) {
  150. return false;
  151. }
  152. stream = ((ConnectorResource) resource).getStream();
  153. String contentDisposition = stream
  154. .getParameter(DownloadStream.CONTENT_DISPOSITION);
  155. if (contentDisposition == null) {
  156. contentDisposition = "attachment; " + DownloadStream
  157. .getContentDispositionFilename(stream.getFileName());
  158. }
  159. stream.setParameter(DownloadStream.CONTENT_DISPOSITION,
  160. contentDisposition);
  161. // Content-Type to block eager browser plug-ins from hijacking
  162. // the file
  163. if (isOverrideContentType()) {
  164. stream.setContentType("application/octet-stream;charset=UTF-8");
  165. }
  166. } finally {
  167. session.unlock();
  168. }
  169. stream.writeResponse(request, response);
  170. return true;
  171. }
  172. @Override
  173. protected FileDownloaderState getState() {
  174. return (FileDownloaderState) super.getState();
  175. }
  176. @Override
  177. protected FileDownloaderState getState(boolean markAsDirty) {
  178. return (FileDownloaderState) super.getState(markAsDirty);
  179. }
  180. }