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 35KB

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