@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); }
openConnection(); int bytesToRead = Math.min(getChunkSize(), bytesRemainingForRequest); finishConnection();
@Test public void testTusUploader() throws IOException, ProtocolException { byte[] content = "hello world".getBytes(); mockServer.when(new HttpRequest() .withPath("/files/foo") .withHeader("Tus-Resumable", TusClient.TUS_VERSION) .withHeader("Upload-Offset", "3") .withHeader("Content-Type", "application/offset+octet-stream") .withHeader(isOpenJDK6 ? "": "Expect: 100-continue") .withBody(Arrays.copyOfRange(content, 3, 11))) .respond(new HttpResponse() .withStatusCode(204) .withHeader("Tus-Resumable", TusClient.TUS_VERSION) .withHeader("Upload-Offset", "11")); TusClient client = new TusClient(); URL uploadUrl = new URL(mockServerURL + "/foo"); TusInputStream input = new TusInputStream(new ByteArrayInputStream(content)); long offset = 3; TusUploader uploader = new TusUploader(client, uploadUrl, input, offset); uploader.setChunkSize(5); assertEquals(uploader.getChunkSize(), 5); assertEquals(5, uploader.uploadChunk()); assertEquals(3, uploader.uploadChunk(5)); assertEquals(-1, uploader.uploadChunk()); assertEquals(-1, uploader.uploadChunk(5)); assertEquals(11, uploader.getOffset()); uploader.finish(); }
TusInputStream input = new TusInputStream(new ByteArrayInputStream(content)); TusUploader uploader = new TusUploader(client, uploadUrl, input, 0); assertEquals(uploader.getRequestPayloadSize(), 10 * 1024 * 1024); uploader.setRequestPayloadSize(5); assertEquals(uploader.getRequestPayloadSize(), 5); uploader.setChunkSize(4); assertEquals(4, uploader.uploadChunk()); assertEquals(1, uploader.uploadChunk()); uploader.setChunkSize(100); assertEquals(5, uploader.uploadChunk()); assertEquals(1, uploader.uploadChunk()); uploader.finish();
@Test public void testMissingUploadOffsetHeader() throws Exception { byte[] content = "hello world".getBytes(); mockServer.when(new HttpRequest() .withPath("/files/missingHeader")) .respond(new HttpResponse() .withStatusCode(204) .withHeader("Tus-Resumable", TusClient.TUS_VERSION)); TusClient client = new TusClient(); URL uploadUrl = new URL(mockServerURL + "/missingHeader"); TusInputStream input = new TusInputStream(new ByteArrayInputStream(content)); TusUploader uploader = new TusUploader(client, uploadUrl, input, 0); boolean exceptionThrown = false; try { assertEquals(11, uploader.uploadChunk()); uploader.finish(); } catch(ProtocolException e) { assertTrue(e.getMessage().contains("no or invalid Upload-Offset header")); exceptionThrown = true; } finally { assertTrue(exceptionThrown); } }
@Test public void testBeginOrResumeUploadFromURL() throws IOException, ProtocolException { mockServer.when(new HttpRequest() .withMethod("HEAD") .withPath("/files/fooFromURL") .withHeader("Tus-Resumable", TusClient.TUS_VERSION)) .respond(new HttpResponse() .withStatusCode(204) .withHeader("Tus-Resumable", TusClient.TUS_VERSION) .withHeader("Upload-Offset", "3")); TusClient client = new TusClient(); URL uploadURL = new URL(mockServerURL.toString() + "/fooFromURL"); TusUpload upload = new TusUpload(); upload.setSize(10); upload.setInputStream(new ByteArrayInputStream(new byte[10])); TusUploader uploader = client.beginOrResumeUploadFromURL(upload, uploadURL); assertEquals(uploader.getUploadURL(), uploadURL); assertEquals(uploader.getOffset(), 3); }
@Test public void testCreateUpload() throws IOException, ProtocolException { mockServer.when(new HttpRequest() .withMethod("POST") .withPath("/files") .withHeader("Tus-Resumable", TusClient.TUS_VERSION) .withHeader("Upload-Metadata", "foo aGVsbG8=,bar d29ybGQ=") .withHeader("Upload-Length", "10")) .respond(new HttpResponse() .withStatusCode(201) .withHeader("Tus-Resumable", TusClient.TUS_VERSION) .withHeader("Location", mockServerURL + "/foo")); Map<String, String> metadata = new LinkedHashMap<String, String>(); metadata.put("foo", "hello"); metadata.put("bar", "world"); TusClient client = new TusClient(); client.setUploadCreationURL(mockServerURL); TusUpload upload = new TusUpload(); upload.setSize(10); upload.setInputStream(new ByteArrayInputStream(new byte[10])); upload.setMetadata(metadata); TusUploader uploader = client.createUpload(upload); assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "/foo")); }
@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); } }
/** * 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); }
return new TusUploader(this, uploadURL, upload.getTusInputStream(), offset);
/** * 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(); }
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 public void testUnmatchingUploadOffsetHeader() throws Exception { byte[] content = "hello world".getBytes(); mockServer.when(new HttpRequest() .withPath("/files/unmatchingHeader")) .respond(new HttpResponse() .withStatusCode(204) .withHeader("Tus-Resumable", TusClient.TUS_VERSION) .withHeader("Upload-Offset", "44")); TusClient client = new TusClient(); URL uploadUrl = new URL(mockServerURL + "/unmatchingHeader"); TusInputStream input = new TusInputStream(new ByteArrayInputStream(content)); TusUploader uploader = new TusUploader(client, uploadUrl, input, 0); boolean exceptionThrown = false; try { assertEquals(11, uploader.uploadChunk()); uploader.finish(); } catch(ProtocolException e) { assertTrue(e.getMessage().contains("different Upload-Offset value (44) than expected (11)")); exceptionThrown = true; } finally { assertTrue(exceptionThrown); } } }
@Test public void testResumeUpload() throws ResumingNotEnabledException, FingerprintNotFoundException, IOException, ProtocolException { mockServer.when(new HttpRequest() .withMethod("HEAD") .withPath("/files/foo") .withHeader("Tus-Resumable", TusClient.TUS_VERSION)) .respond(new HttpResponse() .withStatusCode(204) .withHeader("Tus-Resumable", TusClient.TUS_VERSION) .withHeader("Upload-Offset", "3")); TusClient client = new TusClient(); client.setUploadCreationURL(mockServerURL); client.enableResuming(new TestResumeUploadStore()); TusUpload upload = new TusUpload(); upload.setSize(10); upload.setInputStream(new ByteArrayInputStream(new byte[10])); upload.setFingerprint("test-fingerprint"); TusUploader uploader = client.resumeUpload(upload); assertEquals(uploader.getUploadURL(), new URL(mockServerURL.toString() + "/foo")); assertEquals(uploader.getOffset(), 3); }
@Test public void testResumeOrCreateUpload() throws IOException, ProtocolException { mockServer.when(new HttpRequest() .withMethod("POST") .withPath("/files") .withHeader("Tus-Resumable", TusClient.TUS_VERSION) .withHeader("Upload-Length", "10")) .respond(new HttpResponse() .withStatusCode(201) .withHeader("Tus-Resumable", TusClient.TUS_VERSION) .withHeader("Location", mockServerURL + "/foo")); TusClient client = new TusClient(); client.setUploadCreationURL(mockServerURL); TusUpload upload = new TusUpload(); upload.setSize(10); upload.setInputStream(new ByteArrayInputStream(new byte[10])); TusUploader uploader = client.resumeOrCreateUpload(upload); assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "/foo")); }
return new TusUploader(this, uploadURL, upload.getTusInputStream(), 0);
assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "Redirected/foo"));
TusUploader uploader = client.createUpload(upload); assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "/foo"));