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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235
  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. * Instructs the upload component to 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 upload component's internal submit button by setting its
  928. * caption to null with {@link #setButtonCaption(String)} method.
  929. * <p>
  930. * Note that the upload runs asynchronously. Developer should use normal
  931. * upload listeners to track the process of upload. If the file name field
  932. * is empty, no upload will be triggered.
  933. * <p>
  934. * Also note that the developer should not remove or modify the upload
  935. * component in the same user transaction where the upload submit is
  936. * requested. The upload component can be safely hidden or removed once the
  937. * upload started event has been fired.
  938. */
  939. public void submitUpload() {
  940. markAsDirty();
  941. getRpcProxy(UploadClientRpc.class).submitUpload();
  942. }
  943. @Override
  944. public void markAsDirty() {
  945. super.markAsDirty();
  946. }
  947. /*
  948. * Handle to terminal via Upload monitors and controls the upload during it
  949. * is being streamed.
  950. */
  951. private com.vaadin.server.StreamVariable streamVariable;
  952. protected com.vaadin.server.StreamVariable getStreamVariable() {
  953. if (streamVariable == null) {
  954. streamVariable = new com.vaadin.server.StreamVariable() {
  955. private StreamingStartEvent lastStartedEvent;
  956. @Override
  957. public boolean listenProgress() {
  958. return progressListeners != null
  959. && !progressListeners.isEmpty();
  960. }
  961. @Override
  962. public void onProgress(StreamingProgressEvent event) {
  963. fireUpdateProgress(event.getBytesReceived(),
  964. event.getContentLength());
  965. }
  966. @Override
  967. public boolean isInterrupted() {
  968. return interrupted;
  969. }
  970. @Override
  971. public OutputStream getOutputStream() {
  972. if (getReceiver() == null) {
  973. throw new IllegalStateException(
  974. "Upload cannot be performed without a receiver set");
  975. }
  976. OutputStream receiveUpload = getReceiver().receiveUpload(
  977. lastStartedEvent.getFileName(),
  978. lastStartedEvent.getMimeType());
  979. lastStartedEvent = null;
  980. return receiveUpload;
  981. }
  982. @Override
  983. public void streamingStarted(StreamingStartEvent event) {
  984. startUpload();
  985. contentLength = event.getContentLength();
  986. fireStarted(event.getFileName(), event.getMimeType());
  987. lastStartedEvent = event;
  988. }
  989. @Override
  990. public void streamingFinished(StreamingEndEvent event) {
  991. fireUploadSuccess(event.getFileName(), event.getMimeType(),
  992. event.getContentLength());
  993. endUpload();
  994. if (lastStartedEvent != null)
  995. lastStartedEvent.disposeStreamVariable();
  996. }
  997. @Override
  998. public void streamingFailed(StreamingErrorEvent event) {
  999. try {
  1000. Exception exception = event.getException();
  1001. if (exception instanceof NoInputStreamException) {
  1002. fireNoInputStream(event.getFileName(),
  1003. event.getMimeType(), 0);
  1004. } else if (exception instanceof NoOutputStreamException) {
  1005. fireNoOutputStream(event.getFileName(),
  1006. event.getMimeType(), 0);
  1007. } else {
  1008. fireUploadInterrupted(event.getFileName(),
  1009. event.getMimeType(), 0, exception);
  1010. }
  1011. } finally {
  1012. endUpload();
  1013. if (lastStartedEvent != null)
  1014. lastStartedEvent.disposeStreamVariable();
  1015. }
  1016. }
  1017. };
  1018. }
  1019. return streamVariable;
  1020. }
  1021. @Override
  1022. public java.util.Collection<?> getListeners(java.lang.Class<?> eventType) {
  1023. if (StreamingProgressEvent.class.isAssignableFrom(eventType)) {
  1024. if (progressListeners == null) {
  1025. return Collections.emptyList();
  1026. } else {
  1027. return Collections.unmodifiableCollection(progressListeners);
  1028. }
  1029. }
  1030. return super.getListeners(eventType);
  1031. }
  1032. /**
  1033. * Sets the immediate mode of the upload.
  1034. * <p>
  1035. * If the upload is in immediate mode, the file upload is started
  1036. * immediately after the user has selected the file.
  1037. * <p>
  1038. * If the upload is not in immediate mode, after selecting the file the user
  1039. * must click another button to start the upload.
  1040. * <p>
  1041. * The default mode of an Upload component is immediate.
  1042. *
  1043. * @param immediateMode
  1044. * {@code true} for immediate mode, {@code false} for not
  1045. * @since 8.0
  1046. */
  1047. public void setImmediateMode(boolean immediateMode) {
  1048. getState().immediateMode = immediateMode;
  1049. }
  1050. /**
  1051. * Returns the immediate mode of the upload.
  1052. * <p>
  1053. * The default mode of an Upload component is immediate.
  1054. *
  1055. * @return {@code true} if the upload is in immediate mode, {@code false} if
  1056. * the upload is not in immediate mode
  1057. * @see #setImmediateMode(boolean)
  1058. * @since 8.0
  1059. */
  1060. public boolean isImmediateMode() {
  1061. return getState(false).immediateMode;
  1062. }
  1063. @Override
  1064. protected UploadState getState() {
  1065. return (UploadState) super.getState();
  1066. }
  1067. @Override
  1068. protected UploadState getState(boolean markAsDirty) {
  1069. return (UploadState) super.getState(markAsDirty);
  1070. }
  1071. /**
  1072. * Returns the component's list of accepted content-types. According to RFC
  1073. * 1867, if the attribute is present, the browser might constrain the file
  1074. * patterns prompted for to match those with the corresponding appropriate
  1075. * file extensions for the platform.
  1076. *
  1077. * @return comma-separated list of desired mime types to be uploaded
  1078. * @see #setAcceptMimeTypes
  1079. * @since 8.5
  1080. */
  1081. public String getAcceptMimeTypes() {
  1082. return getState(false).acceptMimeTypes;
  1083. }
  1084. /**
  1085. * Sets the component's list of accepted content-types. According to RFC
  1086. * 1867, if the attribute is present, the browser might constrain the file
  1087. * patterns prompted for to match those with the corresponding appropriate
  1088. * file extensions for the platform. Good examples are: {@code image/*} or
  1089. * {@code image/png,text/plain}
  1090. *
  1091. * @param acceptMimeTypes
  1092. * comma-separated list of desired mime types to be uploaded
  1093. * @see #getAcceptMimeTypes
  1094. * @since 8.5
  1095. */
  1096. public void setAcceptMimeTypes(String acceptMimeTypes) {
  1097. getState().acceptMimeTypes = acceptMimeTypes;
  1098. }
  1099. }