/** * Set the source from which will be read if the file will be later uploaded. * * @param inputStream The stream which will be read. */ public void setInputStream(InputStream inputStream) { input = inputStream; tusInputStream = new TusInputStream(inputStream); }
/** * Finish the request by closing the HTTP connection and the InputStream. * You can call this method even before the entire file has been uploaded. Use this behavior to * enable pausing uploads. * * @throws ProtocolException Thrown if the server sends an unexpected status * code * @throws IOException Thrown if an exception occurs while cleaning up. */ public void finish() throws ProtocolException, IOException { finishConnection(); // Close the TusInputStream after checking the response and closing the connection to ensure // that we will not need to read from it again in the future. input.close(); }
/** * Begin a new upload request by opening a PATCH request to specified upload URL. After this * method returns a connection will be ready and you can upload chunks of the file. * * @param client Used for preparing a request ({@link TusClient#prepareConnection(HttpURLConnection)} * @param uploadURL URL to send the request to * @param input Stream to read (and seek) from and upload to the remote server * @param offset Offset to read from * @throws IOException Thrown if an exception occurs while issuing the HTTP request. */ public TusUploader(TusClient client, URL uploadURL, TusInputStream input, long offset) throws IOException { this.uploadURL = uploadURL; this.input = input; this.offset = offset; this.client = client; input.seekTo(offset); setChunkSize(2 * 1024 * 1024); }
@Override protected void makeAttempt() throws ProtocolException, IOException { // First try to resume an upload. If that's not possible we will create a new // upload and get a TusUploader in return. This class is responsible for opening // a connection to the remote server and doing the uploading. TusUploader uploader = client.resumeOrCreateUpload(upload); // Upload the file in chunks of 1KB sizes. uploader.setChunkSize(1024); // Upload the file as long as data is available. Once the // file has been fully uploaded the method will return -1 do { // Calculate the progress using the total size of the uploading file and // the current offset. long totalBytes = upload.getSize(); long bytesUploaded = uploader.getOffset(); double progress = (double) bytesUploaded / totalBytes * 100; System.out.printf("Upload at %06.2f%%.\n", progress); } while(uploader.uploadChunk() > -1); // Allow the HTTP connection to be closed and cleaned up uploader.finish(); System.out.println("Upload finished."); System.out.format("Upload available at: %s", uploader.getUploadURL().toString()); } };
@Test(expected = IllegalStateException.class) public void testSetRequestPayloadSizeThrows() throws Exception { byte[] content = "hello world".getBytes(); TusClient client = new TusClient(); URL uploadUrl = new URL(mockServerURL + "/payloadException"); TusInputStream input = new TusInputStream(new ByteArrayInputStream(content)); TusUploader uploader = new TusUploader(client, uploadUrl, input, 0); uploader.setChunkSize(4); uploader.uploadChunk(); // Throws IllegalStateException uploader.setRequestPayloadSize(100); }
@Test public void testTusUploaderFailedExpectation() throws IOException, ProtocolException { Assume.assumeFalse(isOpenJDK6); FailingExpectationServer server = new FailingExpectationServer(); server.start(); byte[] content = "hello world".getBytes(); TusClient client = new TusClient(); URL uploadUrl = new URL(server.getURL() + "/expect"); TusInputStream input = new TusInputStream(new ByteArrayInputStream(content)); long offset = 3; boolean exceptionThrown = false; TusUploader uploader = new TusUploader(client, uploadUrl, input, offset); try { uploader.uploadChunk(); } catch(ProtocolException e) { assertTrue(e.getMessage().contains("500")); exceptionThrown = true; } finally { assertTrue(exceptionThrown); } }
/** * Try to resume an already started upload. Before call this function, resuming must be * enabled using {@link #enableResuming(TusURLStore)}. This method will look up the URL for this * upload in the {@link TusURLStore} using the upload's fingerprint (see * {@link TusUpload#getFingerprint()}). After a successful lookup a HEAD request will be issued * to find the current offset without uploading the file, yet. * * @param upload The file for which an upload will be resumed * @return Use {@link TusUploader} to upload the remaining file's chunks. * @throws FingerprintNotFoundException Thrown if no matching fingerprint has been found in * {@link TusURLStore}. Use {@link #createUpload(TusUpload)} to create a new upload. * @throws ResumingNotEnabledException Throw if resuming has not been enabled using {@link * #enableResuming(TusURLStore)}. * @throws ProtocolException Thrown if the remote server sent an unexpected response, e.g. * wrong status codes or missing/invalid headers. * @throws IOException Thrown if an exception occurs while issuing the HTTP request. */ public TusUploader resumeUpload(@NotNull TusUpload upload) throws FingerprintNotFoundException, ResumingNotEnabledException, ProtocolException, IOException { if (!resumingEnabled) { throw new ResumingNotEnabledException(); } URL uploadURL = urlStore.get(upload.getFingerprint()); if (uploadURL == null) { throw new FingerprintNotFoundException(upload.getFingerprint()); } return beginOrResumeUploadFromURL(upload, uploadURL); }
@Test public void testEnableResuming() { TusClient client = new TusClient(); assertEquals(client.resumingEnabled(), false); TusURLStore store = new TusURLMemoryStore(); client.enableResuming(store); assertEquals(client.resumingEnabled(), true); client.disableResuming(); assertEquals(client.resumingEnabled(), false); }
@Test public void test() throws MalformedURLException { TusURLStore store = new TusURLMemoryStore(); URL url = new URL("https://master.tus.io/files/hello"); String fingerprint = "foo"; store.set(fingerprint, url); assertEquals(store.get(fingerprint), url); store.remove(fingerprint); assertEquals(store.get(fingerprint), null); } }
@Test public void testTusClientURL() throws MalformedURLException { TusClient client = new TusClient(); client.setUploadCreationURL(creationUrl); assertEquals(client.getUploadCreationURL(), creationUrl); }
/** * Try to resume an upload using {@link #resumeUpload(TusUpload)}. If the method call throws * an {@link ResumingNotEnabledException} or {@link FingerprintNotFoundException}, a new upload * will be created using {@link #createUpload(TusUpload)}. * * @param upload The file for which an upload will be resumed * @throws ProtocolException Thrown if the remote server sent an unexpected response, e.g. * wrong status codes or missing/invalid headers. * @throws IOException Thrown if an exception occurs while issuing the HTTP request. */ public TusUploader resumeOrCreateUpload(@NotNull TusUpload upload) throws ProtocolException, IOException { try { return resumeUpload(upload); } catch(FingerprintNotFoundException e) { return createUpload(upload); } catch(ResumingNotEnabledException e) { return createUpload(upload); } catch(ProtocolException e) { // If the attempt to resume returned a 404 Not Found, we immediately try to create a new // one since TusExectuor would not retry this operation. HttpURLConnection connection = e.getCausingConnection(); if(connection != null && connection.getResponseCode() == 404) { return createUpload(upload); } throw e; } }
@Override protected void makeAttempt() throws ProtocolException, IOException { super.makeAttempt(); throw new ProtocolException("something happened", new MockHttpURLConnection(500)); } };
@Test public void testMakeAttempts() throws Exception { CountingExecutor exec = new CountingExecutor(); exec.setDelays(new int[]{1, 2, 3}); assertTrue(exec.makeAttempts()); assertEquals(exec.getCalls(), 1); }
@Test public void testSetDelays() { CountingExecutor exec = new CountingExecutor(); assertArrayEquals(exec.getDelays(), new int[]{500, 1000, 2000, 3000}); exec.setDelays(new int[]{1, 2, 3}); assertArrayEquals(exec.getDelays(), new int[]{1, 2, 3}); assertEquals(exec.getCalls(), 0); }
private void finishConnection() throws ProtocolException, IOException { if(output != null) output.close(); if(connection != null) { int responseCode = connection.getResponseCode(); connection.disconnect(); if (!(responseCode >= 200 && responseCode < 300)) { throw new ProtocolException("unexpected status code (" + responseCode + ") while uploading chunk", connection); } // TODO detect changes and seek accordingly long serverOffset = getHeaderFieldLong(connection, "Upload-Offset"); if(serverOffset == -1) { throw new ProtocolException("response to PATCH request contains no or invalid Upload-Offset header", connection); } if(offset != serverOffset) { throw new ProtocolException( String.format("response contains different Upload-Offset value (%d) than expected (%d)", serverOffset, offset), connection); } connection = null; } }
@Test(expected = ProtocolException.class) public void testMakeOneAttempt() throws Exception { CountingExecutor exec = new CountingExecutor() { @Override protected void makeAttempt() throws ProtocolException, IOException { super.makeAttempt(); throw new ProtocolException("something happened", new MockHttpURLConnection(404)); } }; exec.setDelays(new int[]{1, 2, 3}); try { exec.makeAttempts(); } finally { assertEquals(exec.getCalls(), 1); } }
@Test public void testTusClient() { TusClient client = new TusClient(); assertEquals(client.getUploadCreationURL(), null); }
@Override protected void makeAttempt() throws ProtocolException, IOException { super.makeAttempt(); throw new IOException(); } };
@Test public void testSetUploadCreationURL() throws MalformedURLException { TusClient client = new TusClient(); client.setUploadCreationURL(new URL("http://master.tus.io")); assertEquals(client.getUploadCreationURL(), new URL("http://master.tus.io")); }
@Override protected void makeAttempt() throws ProtocolException, IOException { super.makeAttempt(); throw new ProtocolException("something happened", new MockHttpURLConnection(404)); } };