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.

Upload.java 37KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261
  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.ui;
  17. import java.io.OutputStream;
  18. import java.io.Serializable;
  19. import java.lang.reflect.Method;
  20. import java.util.Collections;
  21. import java.util.LinkedHashSet;
  22. import java.util.Map;
  23. import java.util.Objects;
  24. import com.vaadin.server.NoInputStreamException;
  25. import com.vaadin.server.NoOutputStreamException;
  26. import com.vaadin.server.PaintException;
  27. import com.vaadin.server.PaintTarget;
  28. import com.vaadin.server.StreamVariable.StreamingProgressEvent;
  29. import com.vaadin.shared.EventId;
  30. import com.vaadin.shared.Registration;
  31. import com.vaadin.shared.ui.upload.UploadClientRpc;
  32. import com.vaadin.shared.ui.upload.UploadServerRpc;
  33. import com.vaadin.shared.ui.upload.UploadState;
  34. import com.vaadin.util.ReflectTools;
  35. /**
  36. * Component for uploading files from client to server.
  37. *
  38. * <p>
  39. * The visible component consists of a file name input box and a browse button
  40. * and an upload submit button to start uploading.
  41. *
  42. * <p>
  43. * The Upload component needs a java.io.OutputStream to write the uploaded data.
  44. * You need to implement the Upload.Receiver interface and return the output
  45. * stream in the receiveUpload() method.
  46. *
  47. * <p>
  48. * You can get an event regarding starting (StartedEvent), progress
  49. * (ProgressEvent), and finishing (FinishedEvent) of upload by implementing
  50. * StartedListener, ProgressListener, and FinishedListener, respectively. The
  51. * FinishedListener is called for both failed and succeeded uploads. If you wish
  52. * to separate between these two cases, you can use SucceededListener
  53. * (SucceededEvenet) and FailedListener (FailedEvent).
  54. *
  55. * <p>
  56. * The upload component does not itself show upload progress, but you can use
  57. * the ProgressIndicator for providing progress feedback by implementing
  58. * ProgressListener and updating the indicator in updateProgress().
  59. *
  60. * <p>
  61. * Setting upload component immediate with {@link #setImmediateMode(boolean)}
  62. * initiates the upload as soon as a file is selected, instead of the common
  63. * pattern of file selection field and upload button.
  64. *
  65. * <p>
  66. * Note! Because of browser dependent implementations of <input type="file">
  67. * element, setting size for Upload component is not supported. For some
  68. * browsers setting size may work to some extend.
  69. *
  70. * @author Vaadin Ltd.
  71. * @since 3.0
  72. */
  73. @SuppressWarnings("serial")
  74. public class Upload extends AbstractComponent
  75. implements Component.Focusable, LegacyComponent {
  76. /**
  77. * Should the field be focused on next repaint?
  78. */
  79. private final boolean focus = false;
  80. /**
  81. * The tab order number of this field.
  82. */
  83. private int tabIndex = 0;
  84. /**
  85. * The output of the upload is redirected to this receiver.
  86. */
  87. private Receiver receiver;
  88. private boolean isUploading;
  89. private long contentLength = -1;
  90. private int totalBytes;
  91. /**
  92. * ProgressListeners to which information about progress is sent during
  93. * upload
  94. */
  95. private LinkedHashSet<ProgressListener> progressListeners;
  96. private boolean interrupted = false;
  97. private boolean notStarted;
  98. private int nextid;
  99. /**
  100. * Creates a new instance of Upload.
  101. *
  102. * The receiver must be set before performing an upload.
  103. */
  104. public Upload() {
  105. registerRpc(new UploadServerRpc() {
  106. @Override
  107. public void change(String filename) {
  108. fireEvent(new ChangeEvent(Upload.this, filename));
  109. }
  110. @Override
  111. public void poll() {
  112. // Nothing to do, called only to visit the server
  113. }
  114. });
  115. }
  116. public Upload(String caption, Receiver uploadReceiver) {
  117. this();
  118. setCaption(caption);
  119. receiver = uploadReceiver;
  120. }
  121. /**
  122. * Invoked when the value of a variable has changed.
  123. *
  124. * @see com.vaadin.ui.LegacyComponent#changeVariables(java.lang.Object,
  125. * java.util.Map)
  126. */
  127. @Override
  128. public void changeVariables(Object source, Map<String, Object> variables) {
  129. if (variables.containsKey("pollForStart")) {
  130. int id = (Integer) variables.get("pollForStart");
  131. if (!isUploading && id == nextid) {
  132. notStarted = true;
  133. markAsDirty();
  134. } else {
  135. }
  136. }
  137. }
  138. /**
  139. * Paints the content of this component.
  140. *
  141. * @param target
  142. * Target to paint the content on.
  143. * @throws PaintException
  144. * if the paint operation failed.
  145. */
  146. @Override
  147. public void paintContent(PaintTarget target) throws PaintException {
  148. if (notStarted) {
  149. target.addAttribute("notStarted", true);
  150. notStarted = false;
  151. return;
  152. }
  153. // The field should be focused
  154. if (focus) {
  155. target.addAttribute("focus", true);
  156. }
  157. // The tab ordering number
  158. if (tabIndex >= 0) {
  159. target.addAttribute("tabindex", tabIndex);
  160. }
  161. target.addAttribute("state", isUploading);
  162. target.addAttribute("nextid", nextid);
  163. // Post file to this stream variable
  164. target.addVariable(this, "action", getStreamVariable());
  165. }
  166. /**
  167. * Interface that must be implemented by the upload receivers to provide the
  168. * Upload component an output stream to write the uploaded data.
  169. *
  170. * @author Vaadin Ltd.
  171. * @since 3.0
  172. */
  173. @FunctionalInterface
  174. public interface Receiver extends Serializable {
  175. /**
  176. * Invoked when a new upload arrives.
  177. *
  178. * @param filename
  179. * the desired filename of the upload, usually as specified
  180. * by the client.
  181. * @param mimeType
  182. * the MIME type of the uploaded file.
  183. * @return Stream to which the uploaded file should be written.
  184. */
  185. public OutputStream receiveUpload(String filename, String mimeType);
  186. }
  187. /* Upload events */
  188. private static final Method UPLOAD_FINISHED_METHOD;
  189. private static final Method UPLOAD_FAILED_METHOD;
  190. private static final Method UPLOAD_SUCCEEDED_METHOD;
  191. private static final Method UPLOAD_STARTED_METHOD;
  192. static {
  193. try {
  194. UPLOAD_FINISHED_METHOD = FinishedListener.class
  195. .getDeclaredMethod("uploadFinished", FinishedEvent.class);
  196. UPLOAD_FAILED_METHOD = FailedListener.class
  197. .getDeclaredMethod("uploadFailed", FailedEvent.class);
  198. UPLOAD_STARTED_METHOD = StartedListener.class
  199. .getDeclaredMethod("uploadStarted", StartedEvent.class);
  200. UPLOAD_SUCCEEDED_METHOD = SucceededListener.class
  201. .getDeclaredMethod("uploadSucceeded", SucceededEvent.class);
  202. } catch (final NoSuchMethodException e) {
  203. // This should never happen
  204. throw new RuntimeException(
  205. "Internal error finding methods in Upload");
  206. }
  207. }
  208. /**
  209. * Upload.FinishedEvent is sent when the upload receives a file, regardless
  210. * of whether the reception was successful or failed. If you wish to
  211. * distinguish between the two cases, use either SucceededEvent or
  212. * FailedEvent, which are both subclasses of the FinishedEvent.
  213. *
  214. * @author Vaadin Ltd.
  215. * @since 3.0
  216. */
  217. public static class FinishedEvent extends Component.Event {
  218. /**
  219. * Length of the received file.
  220. */
  221. private final long length;
  222. /**
  223. * MIME type of the received file.
  224. */
  225. private final String type;
  226. /**
  227. * Received file name.
  228. */
  229. private final String filename;
  230. /**
  231. *
  232. * @param source
  233. * the source of the file.
  234. * @param filename
  235. * the received file name.
  236. * @param mimeType
  237. * the MIME type of the received file.
  238. * @param length
  239. * the length of the received file.
  240. */
  241. public FinishedEvent(Upload source, String filename, String mimeType,
  242. long length) {
  243. super(source);
  244. type = mimeType;
  245. this.filename = filename;
  246. this.length = length;
  247. }
  248. /**
  249. * Uploads where the event occurred.
  250. *
  251. * @return the Source of the event.
  252. */
  253. public Upload getUpload() {
  254. return (Upload) getSource();
  255. }
  256. /**
  257. * Gets the file name.
  258. *
  259. * @return the filename.
  260. */
  261. public String getFilename() {
  262. return filename;
  263. }
  264. /**
  265. * Gets the MIME Type of the file.
  266. *
  267. * @return the MIME type.
  268. */
  269. public String getMIMEType() {
  270. return type;
  271. }
  272. /**
  273. * Gets the length of the file.
  274. *
  275. * @return the length.
  276. */
  277. public long getLength() {
  278. return length;
  279. }
  280. }
  281. /**
  282. * Upload.FailedEvent event is sent when the upload is received, but the
  283. * reception is interrupted for some reason.
  284. *
  285. * @author Vaadin Ltd.
  286. * @since 3.0
  287. */
  288. public static class FailedEvent extends FinishedEvent {
  289. private Exception reason = null;
  290. /**
  291. *
  292. * @param source
  293. * @param filename
  294. * @param mimeType
  295. * @param length
  296. * @param reason
  297. */
  298. public FailedEvent(Upload source, String filename, String mimeType,
  299. long length, Exception reason) {
  300. this(source, filename, mimeType, length);
  301. this.reason = reason;
  302. }
  303. /**
  304. *
  305. * @param source
  306. * @param filename
  307. * @param mimeType
  308. * @param length
  309. */
  310. public FailedEvent(Upload source, String filename, String mimeType,
  311. long length) {
  312. super(source, filename, mimeType, length);
  313. }
  314. /**
  315. * Gets the exception that caused the failure.
  316. *
  317. * @return the exception that caused the failure, null if n/a
  318. */
  319. public Exception getReason() {
  320. return reason;
  321. }
  322. }
  323. /**
  324. * FailedEvent that indicates that an output stream could not be obtained.
  325. */
  326. public static class NoOutputStreamEvent extends FailedEvent {
  327. /**
  328. *
  329. * @param source
  330. * @param filename
  331. * @param mimeType
  332. * @param length
  333. */
  334. public NoOutputStreamEvent(Upload source, String filename,
  335. String mimeType, long length) {
  336. super(source, filename, mimeType, length);
  337. }
  338. }
  339. /**
  340. * FailedEvent that indicates that an input stream could not be obtained.
  341. */
  342. public static class NoInputStreamEvent extends FailedEvent {
  343. /**
  344. *
  345. * @param source
  346. * @param filename
  347. * @param mimeType
  348. * @param length
  349. */
  350. public NoInputStreamEvent(Upload source, String filename,
  351. String mimeType, long length) {
  352. super(source, filename, mimeType, length);
  353. }
  354. }
  355. /**
  356. * Upload.SucceededEvent event is sent when the upload is received
  357. * successfully.
  358. *
  359. * @author Vaadin Ltd.
  360. * @since 3.0
  361. */
  362. public static class SucceededEvent extends FinishedEvent {
  363. /**
  364. *
  365. * @param source
  366. * @param filename
  367. * @param mimeType
  368. * @param length
  369. */
  370. public SucceededEvent(Upload source, String filename, String mimeType,
  371. long length) {
  372. super(source, filename, mimeType, length);
  373. }
  374. }
  375. /**
  376. * Upload.StartedEvent event is sent when the upload is started to received.
  377. *
  378. * @author Vaadin Ltd.
  379. * @since 5.0
  380. */
  381. public static class StartedEvent extends Component.Event {
  382. private final String filename;
  383. private final String type;
  384. /**
  385. * Length of the received file.
  386. */
  387. private final long length;
  388. /**
  389. *
  390. * @param source
  391. * @param filename
  392. * @param mimeType
  393. * @param contentLength
  394. */
  395. public StartedEvent(Upload source, String filename, String mimeType,
  396. long contentLength) {
  397. super(source);
  398. this.filename = filename;
  399. type = mimeType;
  400. length = contentLength;
  401. }
  402. /**
  403. * Uploads where the event occurred.
  404. *
  405. * @return the Source of the event.
  406. */
  407. public Upload getUpload() {
  408. return (Upload) getSource();
  409. }
  410. /**
  411. * Gets the file name.
  412. *
  413. * @return the filename.
  414. */
  415. public String getFilename() {
  416. return filename;
  417. }
  418. /**
  419. * Gets the MIME Type of the file.
  420. *
  421. * @return the MIME type.
  422. */
  423. public String getMIMEType() {
  424. return type;
  425. }
  426. /**
  427. * @return the length of the file that is being uploaded
  428. */
  429. public long getContentLength() {
  430. return length;
  431. }
  432. }
  433. /**
  434. * Upload.ChangeEvent event is sent when the value (filename) of the upload
  435. * changes.
  436. *
  437. * @since 7.2
  438. */
  439. public static class ChangeEvent extends Component.Event {
  440. private final String filename;
  441. public ChangeEvent(Upload source, String filename) {
  442. super(source);
  443. this.filename = filename;
  444. }
  445. /**
  446. * Uploads where the event occurred.
  447. *
  448. * @return the Source of the event.
  449. */
  450. @Override
  451. public Upload getSource() {
  452. return (Upload) super.getSource();
  453. }
  454. /**
  455. * Gets the file name.
  456. *
  457. * @return the filename.
  458. */
  459. public String getFilename() {
  460. return filename;
  461. }
  462. }
  463. /**
  464. * Receives the events when the upload starts.
  465. *
  466. * @author Vaadin Ltd.
  467. * @since 5.0
  468. */
  469. @FunctionalInterface
  470. public interface StartedListener extends Serializable {
  471. /**
  472. * Upload has started.
  473. *
  474. * @param event
  475. * the Upload started event.
  476. */
  477. public void uploadStarted(StartedEvent event);
  478. }
  479. /**
  480. * Receives the events when the uploads are ready.
  481. *
  482. * @author Vaadin Ltd.
  483. * @since 3.0
  484. */
  485. @FunctionalInterface
  486. public interface FinishedListener extends Serializable {
  487. /**
  488. * Upload has finished.
  489. *
  490. * @param event
  491. * the Upload finished event.
  492. */
  493. public void uploadFinished(FinishedEvent event);
  494. }
  495. /**
  496. * Receives events when the uploads are finished, but unsuccessful.
  497. *
  498. * @author Vaadin Ltd.
  499. * @since 3.0
  500. */
  501. @FunctionalInterface
  502. public interface FailedListener extends Serializable {
  503. /**
  504. * Upload has finished unsuccessfully.
  505. *
  506. * @param event
  507. * the Upload failed event.
  508. */
  509. public void uploadFailed(FailedEvent event);
  510. }
  511. /**
  512. * Receives events when the uploads are successfully finished.
  513. *
  514. * @author Vaadin Ltd.
  515. * @since 3.0
  516. */
  517. @FunctionalInterface
  518. public interface SucceededListener extends Serializable {
  519. /**
  520. * Upload successful.
  521. *
  522. * @param event
  523. * the Upload successful event.
  524. */
  525. public void uploadSucceeded(SucceededEvent event);
  526. }
  527. /**
  528. * Listener for {@link ChangeEvent}.
  529. *
  530. * @since 7.2
  531. */
  532. @FunctionalInterface
  533. public interface ChangeListener extends Serializable {
  534. Method FILENAME_CHANGED = ReflectTools.findMethod(ChangeListener.class,
  535. "filenameChanged", ChangeEvent.class);
  536. /**
  537. * A file has been selected but upload has not yet started.
  538. *
  539. * @param event
  540. * the change event
  541. */
  542. public void filenameChanged(ChangeEvent event);
  543. }
  544. /**
  545. * Adds the upload started event listener.
  546. *
  547. * @param listener
  548. * the Listener to be added, not null
  549. * @since 8.0
  550. */
  551. public Registration addStartedListener(StartedListener listener) {
  552. return addListener(StartedEvent.class, listener, UPLOAD_STARTED_METHOD);
  553. }
  554. /**
  555. * Removes the upload started event listener.
  556. *
  557. * @param listener
  558. * the Listener to be removed.
  559. */
  560. @Deprecated
  561. public void removeStartedListener(StartedListener listener) {
  562. removeListener(StartedEvent.class, listener, UPLOAD_STARTED_METHOD);
  563. }
  564. /**
  565. * Adds the upload received event listener.
  566. *
  567. * @param listener
  568. * the Listener to be added, not null
  569. * @since 8.0
  570. */
  571. public Registration addFinishedListener(FinishedListener listener) {
  572. return addListener(FinishedEvent.class, listener,
  573. UPLOAD_FINISHED_METHOD);
  574. }
  575. /**
  576. * Removes the upload received event listener.
  577. *
  578. * @param listener
  579. * the Listener to be removed.
  580. */
  581. @Deprecated
  582. public void removeFinishedListener(FinishedListener listener) {
  583. removeListener(FinishedEvent.class, listener, UPLOAD_FINISHED_METHOD);
  584. }
  585. /**
  586. * Adds the upload interrupted event listener.
  587. *
  588. * @param listener
  589. * the Listener to be added, not null
  590. * @since 8.0
  591. */
  592. public Registration addFailedListener(FailedListener listener) {
  593. return addListener(FailedEvent.class, listener, UPLOAD_FAILED_METHOD);
  594. }
  595. /**
  596. * Removes the upload interrupted event listener.
  597. *
  598. * @param listener
  599. * the Listener to be removed.
  600. */
  601. @Deprecated
  602. public void removeFailedListener(FailedListener listener) {
  603. removeListener(FailedEvent.class, listener, UPLOAD_FAILED_METHOD);
  604. }
  605. /**
  606. * Adds the upload success event listener.
  607. *
  608. * @param listener
  609. * the Listener to be added, not null
  610. * @since 8.0
  611. */
  612. public Registration addSucceededListener(SucceededListener listener) {
  613. return addListener(SucceededEvent.class, listener,
  614. UPLOAD_SUCCEEDED_METHOD);
  615. }
  616. /**
  617. * Removes the upload success event listener.
  618. *
  619. * @param listener
  620. * the Listener to be removed.
  621. */
  622. @Deprecated
  623. public void removeSucceededListener(SucceededListener listener) {
  624. removeListener(SucceededEvent.class, listener, UPLOAD_SUCCEEDED_METHOD);
  625. }
  626. /**
  627. * Adds the upload progress event listener.
  628. *
  629. * @param listener
  630. * the progress listener to be added
  631. * @since 8.0
  632. */
  633. public Registration addProgressListener(ProgressListener listener) {
  634. Objects.requireNonNull(listener, "Listener must not be null.");
  635. if (progressListeners == null) {
  636. progressListeners = new LinkedHashSet<>();
  637. }
  638. progressListeners.add(listener);
  639. return () -> {
  640. if (progressListeners != null) {
  641. progressListeners.remove(listener);
  642. }
  643. };
  644. }
  645. /**
  646. * Removes the upload progress event listener.
  647. *
  648. * @param listener
  649. * the progress listener to be removed
  650. */
  651. @Deprecated
  652. public void removeProgressListener(ProgressListener listener) {
  653. if (progressListeners != null) {
  654. progressListeners.remove(listener);
  655. }
  656. }
  657. /**
  658. * Adds a filename change event listener.
  659. *
  660. * @param listener
  661. * the Listener to add, not null
  662. * @since 8.0
  663. */
  664. public Registration addChangeListener(ChangeListener listener) {
  665. return addListener(EventId.CHANGE, ChangeEvent.class, listener,
  666. ChangeListener.FILENAME_CHANGED);
  667. }
  668. /**
  669. * Removes a filename change event listener.
  670. *
  671. * @param listener
  672. * the listener to be removed
  673. */
  674. @Deprecated
  675. public void removeChangeListener(ChangeListener listener) {
  676. super.removeListener(EventId.CHANGE, ChangeEvent.class, listener);
  677. }
  678. /**
  679. * Emit upload received event.
  680. *
  681. * @param filename
  682. * @param mimeType
  683. */
  684. protected void fireStarted(String filename, String mimeType) {
  685. fireEvent(new Upload.StartedEvent(this, filename, mimeType,
  686. contentLength));
  687. }
  688. /**
  689. * Emits the upload failed event.
  690. *
  691. * @param filename
  692. * @param mimeType
  693. * @param length
  694. */
  695. protected void fireUploadInterrupted(String filename, String mimeType,
  696. long length) {
  697. fireEvent(new Upload.FailedEvent(this, filename, mimeType, length));
  698. }
  699. protected void fireNoInputStream(String filename, String mimeType,
  700. long length) {
  701. fireEvent(new Upload.NoInputStreamEvent(this, filename, mimeType,
  702. length));
  703. }
  704. protected void fireNoOutputStream(String filename, String mimeType,
  705. long length) {
  706. fireEvent(new Upload.NoOutputStreamEvent(this, filename, mimeType,
  707. length));
  708. }
  709. protected void fireUploadInterrupted(String filename, String mimeType,
  710. long length, Exception e) {
  711. fireEvent(new Upload.FailedEvent(this, filename, mimeType, length, e));
  712. }
  713. /**
  714. * Emits the upload success event.
  715. *
  716. * @param filename
  717. * @param MIMEType
  718. * @param length
  719. *
  720. */
  721. protected void fireUploadSuccess(String filename, String MIMEType,
  722. long length) {
  723. fireEvent(new Upload.SucceededEvent(this, filename, MIMEType, length));
  724. }
  725. /**
  726. * Emits the progress event.
  727. *
  728. * @param totalBytes
  729. * bytes received so far
  730. * @param contentLength
  731. * actual size of the file being uploaded, if known
  732. *
  733. */
  734. protected void fireUpdateProgress(long totalBytes, long contentLength) {
  735. // this is implemented differently than other listeners to maintain
  736. // backwards compatibility
  737. if (progressListeners != null) {
  738. for (ProgressListener l : progressListeners) {
  739. l.updateProgress(totalBytes, contentLength);
  740. }
  741. }
  742. }
  743. /**
  744. * Returns the current receiver.
  745. *
  746. * @return the StreamVariable.
  747. */
  748. public Receiver getReceiver() {
  749. return receiver;
  750. }
  751. /**
  752. * Sets the receiver.
  753. *
  754. * @param receiver
  755. * the receiver to set.
  756. */
  757. public void setReceiver(Receiver receiver) {
  758. this.receiver = receiver;
  759. }
  760. /**
  761. * {@inheritDoc}
  762. */
  763. @Override
  764. public void focus() {
  765. super.focus();
  766. }
  767. /**
  768. * Gets the Tabulator index of this Focusable component.
  769. *
  770. * @see com.vaadin.ui.Component.Focusable#getTabIndex()
  771. */
  772. @Override
  773. public int getTabIndex() {
  774. return tabIndex;
  775. }
  776. /**
  777. * Sets the Tabulator index of this Focusable component.
  778. *
  779. * @see com.vaadin.ui.Component.Focusable#setTabIndex(int)
  780. */
  781. @Override
  782. public void setTabIndex(int tabIndex) {
  783. this.tabIndex = tabIndex;
  784. }
  785. /**
  786. * Go into upload state. This is to prevent double uploading on same
  787. * component.
  788. *
  789. * Warning: this is an internal method used by the framework and should not
  790. * be used by user of the Upload component. Using it results in the Upload
  791. * component going in wrong state and not working. It is currently public
  792. * because it is used by another class.
  793. */
  794. public void startUpload() {
  795. if (isUploading) {
  796. throw new IllegalStateException("uploading already started");
  797. }
  798. isUploading = true;
  799. nextid++;
  800. }
  801. /**
  802. * Interrupts the upload currently being received. The interruption will be
  803. * done by the receiving thread so this method will return immediately and
  804. * the actual interrupt will happen a bit later.
  805. */
  806. public void interruptUpload() {
  807. if (isUploading) {
  808. interrupted = true;
  809. }
  810. }
  811. /**
  812. * Go into state where new uploading can begin.
  813. *
  814. * Warning: this is an internal method used by the framework and should not
  815. * be used by user of the Upload component.
  816. */
  817. private void endUpload() {
  818. isUploading = false;
  819. contentLength = -1;
  820. interrupted = false;
  821. markAsDirty();
  822. }
  823. public boolean isUploading() {
  824. return isUploading;
  825. }
  826. /**
  827. * Gets read bytes of the file currently being uploaded.
  828. *
  829. * @return bytes
  830. */
  831. public long getBytesRead() {
  832. return totalBytes;
  833. }
  834. /**
  835. * Returns size of file currently being uploaded. Value sane only during
  836. * upload.
  837. *
  838. * @return size in bytes
  839. */
  840. public long getUploadSize() {
  841. return contentLength;
  842. }
  843. /**
  844. * ProgressListener receives events to track progress of upload.
  845. */
  846. @FunctionalInterface
  847. public interface ProgressListener extends Serializable {
  848. /**
  849. * Updates progress to listener.
  850. *
  851. * @param readBytes
  852. * bytes transferred
  853. * @param contentLength
  854. * total size of file currently being uploaded, -1 if unknown
  855. */
  856. public void updateProgress(long readBytes, long contentLength);
  857. }
  858. /**
  859. * Returns the string rendered into button that fires uploading.
  860. *
  861. * @return String to be rendered into button that fires uploading
  862. */
  863. public String getButtonCaption() {
  864. return getState(false).buttonCaption;
  865. }
  866. /**
  867. * Returns the style name rendered into button that fires uploading.
  868. *
  869. * @return Style name to be rendered into button that fires uploading
  870. * @since 8.2
  871. */
  872. public String getButtonStyleName() {
  873. return getState(false).buttonStyleName;
  874. }
  875. /**
  876. * Checks whether the caption of the button that fires uploading is rendered
  877. * as HTML
  878. * <p>
  879. * The default is {@code false}, i.e. to render that caption as plain text.
  880. *
  881. * @return {@code true} if the caption is rendered as HTML, {@code false} if
  882. * rendered as plain text
  883. * @since
  884. */
  885. public boolean isButtonCaptionAsHtml() {
  886. return getState(false).buttonCaptionAsHtml;
  887. }
  888. /**
  889. * In addition to the actual file chooser, upload components have button
  890. * that starts actual upload progress. This method is used to set text in
  891. * that button.
  892. * <p>
  893. * In case the button text is set to null, the button is hidden. In this
  894. * case developer must explicitly initiate the upload process with
  895. * {@link #submitUpload()}.
  896. * <p>
  897. * In case the Upload is used in immediate mode using
  898. * {@link #setImmediateMode(boolean)}, the file chooser (HTML input with
  899. * type "file") is hidden and only the button with this text is shown.
  900. * <p>
  901. *
  902. * <p>
  903. * <strong>Note</strong> the string given is set as is to the button. HTML
  904. * formatting is not stripped. Be sure to properly validate your value
  905. * according to your needs.
  906. *
  907. * @param buttonCaption
  908. * text for upload components button.
  909. */
  910. public void setButtonCaption(String buttonCaption) {
  911. getState().buttonCaption = buttonCaption;
  912. }
  913. /**
  914. * In addition to the actual file chooser, upload components have button
  915. * that starts actual upload progress. This method is used to set a style
  916. * name to that button.
  917. * <p>
  918. * Note: Unlike {@code Button.setStyleName()} this method overrides all the
  919. * styles from the button. If you wish to preserve the default styles, enter
  920. * the style name as {@code "v-button yourStyleName"}.
  921. *
  922. * @param buttonStyleName
  923. * style name for upload components button.
  924. * @see #setButtonCaption(String) about when the button is shown / hidden.
  925. * @since 8.2
  926. */
  927. public void setButtonStyleName(String buttonStyleName) {
  928. getState().buttonStyleName = buttonStyleName;
  929. }
  930. /**
  931. * In addition to the actual file chooser, upload components have button
  932. * that starts actual upload progress. This method is used to set whether
  933. * the caption on that button is rendered as HTML.
  934. * <p>
  935. * If set to {@code true}, the caption is rendered in the browser as HTML
  936. * and the developer is responsible for ensuring no harmful HTML is used. If
  937. * set to {@code false}, the caption is rendered in the browser as plain
  938. * text.
  939. * <p>
  940. * The default is {@code false}, i.e. to render the caption as plain text.
  941. *
  942. * @param buttonCaptionAsHtml
  943. * {@code true} if the caption is rendered as HTML, {@code false}
  944. * if rendered as plain text
  945. * @since
  946. */
  947. public void setButtonCaptionAsHtml(boolean buttonCaptionAsHtml) {
  948. getState().buttonCaptionAsHtml = buttonCaptionAsHtml;
  949. }
  950. /**
  951. * Instructs the upload component to send selected file to the server.
  952. * <p>
  953. * In case developer wants to use this feature, he/she will most probably
  954. * want to hide the upload component's internal submit button by setting its
  955. * caption to null with {@link #setButtonCaption(String)} method.
  956. * <p>
  957. * Note that the upload runs asynchronously. Developer should use normal
  958. * upload listeners to track the process of upload. If the file name field
  959. * is empty, no upload will be triggered.
  960. * <p>
  961. * Also note that the developer should not remove or modify the upload
  962. * component in the same user transaction where the upload submit is
  963. * requested. The upload component can be safely hidden or removed once the
  964. * upload started event has been fired.
  965. */
  966. public void submitUpload() {
  967. markAsDirty();
  968. getRpcProxy(UploadClientRpc.class).submitUpload();
  969. }
  970. @Override
  971. public void markAsDirty() {
  972. super.markAsDirty();
  973. }
  974. /*
  975. * Handle to terminal via Upload monitors and controls the upload during it
  976. * is being streamed.
  977. */
  978. private com.vaadin.server.StreamVariable streamVariable;
  979. protected com.vaadin.server.StreamVariable getStreamVariable() {
  980. if (streamVariable == null) {
  981. streamVariable = new com.vaadin.server.StreamVariable() {
  982. private StreamingStartEvent lastStartedEvent;
  983. @Override
  984. public boolean listenProgress() {
  985. return progressListeners != null
  986. && !progressListeners.isEmpty();
  987. }
  988. @Override
  989. public void onProgress(StreamingProgressEvent event) {
  990. fireUpdateProgress(event.getBytesReceived(),
  991. event.getContentLength());
  992. }
  993. @Override
  994. public boolean isInterrupted() {
  995. return interrupted;
  996. }
  997. @Override
  998. public OutputStream getOutputStream() {
  999. if (getReceiver() == null) {
  1000. throw new IllegalStateException(
  1001. "Upload cannot be performed without a receiver set");
  1002. }
  1003. OutputStream receiveUpload = getReceiver().receiveUpload(
  1004. lastStartedEvent.getFileName(),
  1005. lastStartedEvent.getMimeType());
  1006. lastStartedEvent = null;
  1007. return receiveUpload;
  1008. }
  1009. @Override
  1010. public void streamingStarted(StreamingStartEvent event) {
  1011. startUpload();
  1012. contentLength = event.getContentLength();
  1013. fireStarted(event.getFileName(), event.getMimeType());
  1014. lastStartedEvent = event;
  1015. }
  1016. @Override
  1017. public void streamingFinished(StreamingEndEvent event) {
  1018. fireUploadSuccess(event.getFileName(), event.getMimeType(),
  1019. event.getContentLength());
  1020. endUpload();
  1021. if (lastStartedEvent != null)
  1022. lastStartedEvent.disposeStreamVariable();
  1023. }
  1024. @Override
  1025. public void streamingFailed(StreamingErrorEvent event) {
  1026. try {
  1027. Exception exception = event.getException();
  1028. if (exception instanceof NoInputStreamException) {
  1029. fireNoInputStream(event.getFileName(),
  1030. event.getMimeType(), 0);
  1031. } else if (exception instanceof NoOutputStreamException) {
  1032. fireNoOutputStream(event.getFileName(),
  1033. event.getMimeType(), 0);
  1034. } else {
  1035. fireUploadInterrupted(event.getFileName(),
  1036. event.getMimeType(), 0, exception);
  1037. }
  1038. } finally {
  1039. endUpload();
  1040. if (lastStartedEvent != null)
  1041. lastStartedEvent.disposeStreamVariable();
  1042. }
  1043. }
  1044. };
  1045. }
  1046. return streamVariable;
  1047. }
  1048. @Override
  1049. public java.util.Collection<?> getListeners(java.lang.Class<?> eventType) {
  1050. if (StreamingProgressEvent.class.isAssignableFrom(eventType)) {
  1051. if (progressListeners == null) {
  1052. return Collections.emptyList();
  1053. } else {
  1054. return Collections.unmodifiableCollection(progressListeners);
  1055. }
  1056. }
  1057. return super.getListeners(eventType);
  1058. }
  1059. /**
  1060. * Sets the immediate mode of the upload.
  1061. * <p>
  1062. * If the upload is in immediate mode, the file upload is started
  1063. * immediately after the user has selected the file.
  1064. * <p>
  1065. * If the upload is not in immediate mode, after selecting the file the user
  1066. * must click another button to start the upload.
  1067. * <p>
  1068. * The default mode of an Upload component is immediate.
  1069. *
  1070. * @param immediateMode
  1071. * {@code true} for immediate mode, {@code false} for not
  1072. * @since 8.0
  1073. */
  1074. public void setImmediateMode(boolean immediateMode) {
  1075. getState().immediateMode = immediateMode;
  1076. }
  1077. /**
  1078. * Returns the immediate mode of the upload.
  1079. * <p>
  1080. * The default mode of an Upload component is immediate.
  1081. *
  1082. * @return {@code true} if the upload is in immediate mode, {@code false} if
  1083. * the upload is not in immediate mode
  1084. * @see #setImmediateMode(boolean)
  1085. * @since 8.0
  1086. */
  1087. public boolean isImmediateMode() {
  1088. return getState(false).immediateMode;
  1089. }
  1090. @Override
  1091. protected UploadState getState() {
  1092. return (UploadState) super.getState();
  1093. }
  1094. @Override
  1095. protected UploadState getState(boolean markAsDirty) {
  1096. return (UploadState) super.getState(markAsDirty);
  1097. }
  1098. /**
  1099. * Returns the component's list of accepted content-types. According to RFC
  1100. * 1867, if the attribute is present, the browser might constrain the file
  1101. * patterns prompted for to match those with the corresponding appropriate
  1102. * file extensions for the platform.
  1103. *
  1104. * @return comma-separated list of desired mime types to be uploaded
  1105. * @see #setAcceptMimeTypes
  1106. * @since 8.5
  1107. */
  1108. public String getAcceptMimeTypes() {
  1109. return getState(false).acceptMimeTypes;
  1110. }
  1111. /**
  1112. * Sets the component's list of accepted content-types. According to RFC
  1113. * 1867, if the attribute is present, the browser might constrain the file
  1114. * patterns prompted for to match those with the corresponding appropriate
  1115. * file extensions for the platform. Good examples are: {@code image/*} or
  1116. * {@code image/png,text/plain}
  1117. *
  1118. * @param acceptMimeTypes
  1119. * comma-separated list of desired mime types to be uploaded
  1120. * @see #getAcceptMimeTypes
  1121. * @since 8.5
  1122. */
  1123. public void setAcceptMimeTypes(String acceptMimeTypes) {
  1124. getState().acceptMimeTypes = acceptMimeTypes;
  1125. }
  1126. }