Skip to content

Commit 2694701

Browse files
authored
Add preload data support for putObject() API. (#1156)
When this flag is enabled, entire part/object data is loaded into memory to enable connection retry on network failure in the middle of upload.
1 parent c8dd70c commit 2694701

File tree

4 files changed

+56
-11
lines changed

4 files changed

+56
-11
lines changed

api/src/main/java/io/minio/MinioClient.java

+33-6
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
import io.minio.messages.VersioningConfiguration;
7777
import io.minio.org.apache.commons.validator.routines.InetAddressValidator;
7878
import java.io.BufferedInputStream;
79+
import java.io.ByteArrayOutputStream;
7980
import java.io.FileInputStream;
8081
import java.io.IOException;
8182
import java.io.InputStream;
@@ -626,9 +627,8 @@ protected Response execute(
626627
traceBuilder.append("\n");
627628

628629
OkHttpClient httpClient = this.httpClient;
629-
if (method == Method.PUT || method == Method.POST) {
630-
// Issue #924: disable connection retry for PUT and POST methods. Its safe to do
631-
// retry for other methods.
630+
if (!(body instanceof byte[]) && (method == Method.PUT || method == Method.POST)) {
631+
// Issue #924: disable connection retry for PUT and POST methods for other than byte array.
632632
httpClient = this.httpClient.newBuilder().retryOnConnectionFailure(false).build();
633633
}
634634

@@ -2676,7 +2676,7 @@ public void removeBucket(RemoveBucketArgs args)
26762676
}
26772677

26782678
private ObjectWriteResponse putObject(
2679-
ObjectWriteArgs args,
2679+
PutObjectBaseArgs args,
26802680
Object data,
26812681
long objectSize,
26822682
long partSize,
@@ -2694,6 +2694,7 @@ private ObjectWriteResponse putObject(
26942694
String uploadId = null;
26952695
long uploadedSize = 0L;
26962696
Part[] parts = null;
2697+
ByteArrayOutputStream buffer = null;
26972698

26982699
try {
26992700
for (int partNumber = 1; partNumber <= partCount || partCount < 0; partNumber++) {
@@ -2714,12 +2715,19 @@ private ObjectWriteResponse putObject(
27142715
}
27152716
}
27162717

2718+
Object body = data;
2719+
if (args.preloadData()) {
2720+
if (buffer == null) buffer = new ByteArrayOutputStream();
2721+
fillData(buffer, data, availableSize);
2722+
body = buffer.toByteArray();
2723+
}
2724+
27172725
if (partCount == 1) {
27182726
return putObject(
27192727
args.bucket(),
27202728
args.region(),
27212729
args.object(),
2722-
data,
2730+
body,
27232731
(int) availableSize,
27242732
headers,
27252733
args.extraQueryParams());
@@ -2744,7 +2752,7 @@ private ObjectWriteResponse putObject(
27442752
args.bucket(),
27452753
args.region(),
27462754
args.object(),
2747-
data,
2755+
body,
27482756
(int) availableSize,
27492757
uploadId,
27502758
partNumber,
@@ -3807,6 +3815,25 @@ private long getAvailableSize(Object data, long expectedReadSize)
38073815
return totalBytesRead;
38083816
}
38093817

3818+
private void fillData(ByteArrayOutputStream buffer, Object data, long size) throws IOException {
3819+
buffer.reset();
3820+
byte[] buf = new byte[16384]; // 16KiB buffer for optimization
3821+
long totalBytesRead = 0;
3822+
while (totalBytesRead < size) {
3823+
long bytesToRead = size - totalBytesRead;
3824+
if (bytesToRead > buf.length) bytesToRead = buf.length;
3825+
int bytesRead = -1;
3826+
if (data instanceof RandomAccessFile) {
3827+
bytesRead = ((RandomAccessFile) data).read(buf, 0, (int) bytesToRead);
3828+
} else if (data instanceof BufferedInputStream) {
3829+
bytesRead = ((BufferedInputStream) data).read(buf, 0, (int) bytesToRead);
3830+
}
3831+
if (bytesRead < 0) throw new IOException("EOF on reading data");
3832+
buffer.write(buf, 0, bytesRead);
3833+
totalBytesRead += bytesRead;
3834+
}
3835+
}
3836+
38103837
/**
38113838
* Sets HTTP connect, write and read timeouts. A value of 0 means no timeout, otherwise values
38123839
* must be between 1 and Integer.MAX_VALUE when converted to milliseconds.

api/src/main/java/io/minio/PutObjectArgs.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public static Builder builder() {
4343
}
4444

4545
/** Argument builder of {@link PutObjectArgs}. */
46-
public static final class Builder extends ObjectWriteArgs.Builder<Builder, PutObjectArgs> {
46+
public static final class Builder extends PutObjectBaseArgs.Builder<Builder, PutObjectArgs> {
4747
@Override
4848
protected void validate(PutObjectArgs args) {
4949
super.validate(args);

api/src/main/java/io/minio/PutObjectBaseArgs.java

+21-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public abstract class PutObjectBaseArgs extends ObjectWriteArgs {
2525
protected long partSize;
2626
protected int partCount;
2727
protected String contentType;
28+
protected boolean preloadData;
2829

2930
public long objectSize() {
3031
return objectSize;
@@ -51,9 +52,24 @@ public String contentType() throws IOException {
5152
return null;
5253
}
5354

55+
public boolean preloadData() {
56+
return preloadData;
57+
}
58+
5459
/** Base argument builder class for {@link PutObjectBaseArgs}. */
60+
@SuppressWarnings("unchecked") // Its safe to type cast to B as B is inherited by this class
5561
public abstract static class Builder<B extends Builder<B, A>, A extends PutObjectBaseArgs>
56-
extends ObjectWriteArgs.Builder<B, A> {}
62+
extends ObjectWriteArgs.Builder<B, A> {
63+
/**
64+
* Sets flag to control data preload of stream/file. When this flag is enabled, entire
65+
* part/object data is loaded into memory to enable connection retry on network failure in the
66+
* middle of upload.
67+
*/
68+
public B preloadData(boolean preloadData) {
69+
operations.add(args -> args.preloadData = preloadData);
70+
return (B) this;
71+
}
72+
}
5773

5874
@Override
5975
public boolean equals(Object o) {
@@ -64,11 +80,13 @@ public boolean equals(Object o) {
6480
return objectSize == that.objectSize
6581
&& partSize == that.partSize
6682
&& partCount == that.partCount
67-
&& Objects.equal(contentType, that.contentType);
83+
&& Objects.equal(contentType, that.contentType)
84+
&& preloadData == that.preloadData;
6885
}
6986

7087
@Override
7188
public int hashCode() {
72-
return Objects.hashCode(super.hashCode(), objectSize, partSize, partCount, contentType);
89+
return Objects.hashCode(
90+
super.hashCode(), objectSize, partSize, partCount, contentType, preloadData);
7391
}
7492
}

api/src/main/java/io/minio/UploadObjectArgs.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public static Builder builder() {
5050
}
5151

5252
/** Argument builder of {@link UploadObjectArgs}. */
53-
public static final class Builder extends ObjectWriteArgs.Builder<Builder, UploadObjectArgs> {
53+
public static final class Builder extends PutObjectBaseArgs.Builder<Builder, UploadObjectArgs> {
5454
@Override
5555
protected void validate(UploadObjectArgs args) {
5656
super.validate(args);

0 commit comments

Comments
 (0)