18
18
19
19
import static com .google .common .base .Preconditions .checkNotNull ;
20
20
21
+ import com .google .cloud .storage .BlobInfo ;
21
22
import com .google .cloud .storage .Storage .BlobWriteOption ;
22
23
import com .google .common .base .MoreObjects ;
23
24
import com .google .common .collect .ImmutableList ;
24
25
import java .util .List ;
25
26
import java .util .Objects ;
27
+ import java .util .function .Function ;
26
28
import org .checkerframework .checker .nullness .qual .NonNull ;
27
29
28
30
/**
33
35
public final class ParallelUploadConfig {
34
36
35
37
private final boolean skipIfExists ;
36
- @ NonNull private final String prefix ;
37
38
@ NonNull private final String bucketName ;
39
+ @ NonNull private final UploadBlobInfoFactory uploadBlobInfoFactory ;
38
40
39
41
@ NonNull private final List <BlobWriteOption > writeOptsPerRequest ;
40
42
41
43
private ParallelUploadConfig (
42
44
boolean skipIfExists ,
43
- @ NonNull String prefix ,
44
45
@ NonNull String bucketName ,
46
+ @ NonNull UploadBlobInfoFactory uploadBlobInfoFactory ,
45
47
@ NonNull List <BlobWriteOption > writeOptsPerRequest ) {
46
48
this .skipIfExists = skipIfExists ;
47
- this .prefix = prefix ;
48
49
this .bucketName = bucketName ;
50
+ this .uploadBlobInfoFactory = uploadBlobInfoFactory ;
49
51
this .writeOptsPerRequest = applySkipIfExists (skipIfExists , writeOptsPerRequest );
50
52
}
51
53
@@ -63,9 +65,26 @@ public boolean isSkipIfExists() {
63
65
* A common prefix that will be applied to all object paths in the destination bucket
64
66
*
65
67
* @see Builder#setPrefix(String)
68
+ * @see Builder#setUploadBlobInfoFactory(UploadBlobInfoFactory)
69
+ * @see UploadBlobInfoFactory#prefixObjectNames(String)
66
70
*/
67
71
public @ NonNull String getPrefix () {
68
- return prefix ;
72
+ if (uploadBlobInfoFactory instanceof PrefixObjectNames ) {
73
+ PrefixObjectNames prefixObjectNames = (PrefixObjectNames ) uploadBlobInfoFactory ;
74
+ return prefixObjectNames .prefix ;
75
+ }
76
+ return "" ;
77
+ }
78
+
79
+ /**
80
+ * The {@link UploadBlobInfoFactory} which will be used to produce a {@link BlobInfo}s based on a
81
+ * provided bucket name and file name.
82
+ *
83
+ * @see Builder#setUploadBlobInfoFactory(UploadBlobInfoFactory)
84
+ * @since 2.49.0
85
+ */
86
+ public @ NonNull UploadBlobInfoFactory getUploadBlobInfoFactory () {
87
+ return uploadBlobInfoFactory ;
69
88
}
70
89
71
90
/**
@@ -96,22 +115,22 @@ public boolean equals(Object o) {
96
115
}
97
116
ParallelUploadConfig that = (ParallelUploadConfig ) o ;
98
117
return skipIfExists == that .skipIfExists
99
- && prefix .equals (that .prefix )
100
118
&& bucketName .equals (that .bucketName )
119
+ && uploadBlobInfoFactory .equals (that .uploadBlobInfoFactory )
101
120
&& writeOptsPerRequest .equals (that .writeOptsPerRequest );
102
121
}
103
122
104
123
@ Override
105
124
public int hashCode () {
106
- return Objects .hash (skipIfExists , prefix , bucketName , writeOptsPerRequest );
125
+ return Objects .hash (skipIfExists , bucketName , uploadBlobInfoFactory , writeOptsPerRequest );
107
126
}
108
127
109
128
@ Override
110
129
public String toString () {
111
130
return MoreObjects .toStringHelper (this )
112
131
.add ("skipIfExists" , skipIfExists )
113
- .add ("prefix" , prefix )
114
132
.add ("bucketName" , bucketName )
133
+ .add ("uploadBlobInfoFactory" , uploadBlobInfoFactory )
115
134
.add ("writeOptsPerRequest" , writeOptsPerRequest )
116
135
.toString ();
117
136
}
@@ -137,13 +156,13 @@ private static List<BlobWriteOption> applySkipIfExists(
137
156
public static final class Builder {
138
157
139
158
private boolean skipIfExists ;
140
- private @ NonNull String prefix ;
141
159
private @ NonNull String bucketName ;
160
+ private @ NonNull UploadBlobInfoFactory uploadBlobInfoFactory ;
142
161
private @ NonNull List <BlobWriteOption > writeOptsPerRequest ;
143
162
144
163
private Builder () {
145
- this .prefix = "" ;
146
164
this .bucketName = "" ;
165
+ this .uploadBlobInfoFactory = UploadBlobInfoFactory .defaultInstance ();
147
166
this .writeOptsPerRequest = ImmutableList .of ();
148
167
}
149
168
@@ -162,11 +181,37 @@ public Builder setSkipIfExists(boolean skipIfExists) {
162
181
/**
163
182
* Sets a common prefix that will be applied to all object paths in the destination bucket.
164
183
*
184
+ * <p><i>NOTE</i>: this method and {@link #setUploadBlobInfoFactory(UploadBlobInfoFactory)} are
185
+ * mutually exclusive, and last invocation "wins".
186
+ *
165
187
* @return the builder instance with the value for prefix modified.
166
188
* @see ParallelUploadConfig#getPrefix()
189
+ * @see ParallelUploadConfig.Builder#setUploadBlobInfoFactory(UploadBlobInfoFactory)
190
+ * @see UploadBlobInfoFactory#prefixObjectNames(String)
167
191
*/
168
192
public Builder setPrefix (@ NonNull String prefix ) {
169
- this .prefix = prefix ;
193
+ this .uploadBlobInfoFactory = UploadBlobInfoFactory .prefixObjectNames (prefix );
194
+ return this ;
195
+ }
196
+
197
+ /**
198
+ * Sets a {@link UploadBlobInfoFactory} which can be used to produce a custom BlobInfo based on
199
+ * a provided bucket name and file name.
200
+ *
201
+ * <p>The bucket name in the returned BlobInfo MUST be equal to the value provided to {@link
202
+ * #setBucketName(String)}, if not that upload will fail with a {@link
203
+ * TransferStatus#FAILED_TO_START} and a {@link BucketNameMismatchException}.
204
+ *
205
+ * <p><i>NOTE</i>: this method and {@link #setPrefix(String)} are mutually exclusive, and last
206
+ * invocation "wins".
207
+ *
208
+ * @return the builder instance with the value for uploadBlobInfoFactory modified.
209
+ * @see ParallelUploadConfig#getPrefix()
210
+ * @see ParallelUploadConfig#getUploadBlobInfoFactory()
211
+ * @since 2.49.0
212
+ */
213
+ public Builder setUploadBlobInfoFactory (@ NonNull UploadBlobInfoFactory uploadBlobInfoFactory ) {
214
+ this .uploadBlobInfoFactory = uploadBlobInfoFactory ;
170
215
return this ;
171
216
}
172
217
@@ -199,10 +244,99 @@ public Builder setWriteOptsPerRequest(@NonNull List<BlobWriteOption> writeOptsPe
199
244
* @return {@link ParallelUploadConfig}
200
245
*/
201
246
public ParallelUploadConfig build () {
202
- checkNotNull (prefix );
203
247
checkNotNull (bucketName );
248
+ checkNotNull (uploadBlobInfoFactory );
204
249
checkNotNull (writeOptsPerRequest );
205
- return new ParallelUploadConfig (skipIfExists , prefix , bucketName , writeOptsPerRequest );
250
+ return new ParallelUploadConfig (
251
+ skipIfExists , bucketName , uploadBlobInfoFactory , writeOptsPerRequest );
252
+ }
253
+ }
254
+
255
+ public interface UploadBlobInfoFactory {
256
+
257
+ /**
258
+ * Method to produce a {@link BlobInfo} to be used for the upload to Cloud Storage.
259
+ *
260
+ * <p>The bucket name in the returned BlobInfo MUST be equal to the value provided to the {@link
261
+ * ParallelUploadConfig.Builder#setBucketName(String)}, if not that upload will fail with a
262
+ * {@link TransferStatus#FAILED_TO_START} and a {@link BucketNameMismatchException}.
263
+ *
264
+ * @param bucketName The name of the bucket to be uploaded to. The value provided here will be
265
+ * the value from {@link ParallelUploadConfig#getBucketName()}.
266
+ * @param fileName The String representation of the absolute path of the file to be uploaded
267
+ * @return The instance of {@link BlobInfo} that should be used to upload the file to Cloud
268
+ * Storage.
269
+ */
270
+ BlobInfo apply (String bucketName , String fileName );
271
+
272
+ /**
273
+ * Adapter factory to provide the same semantics as if using {@link Builder#setPrefix(String)}
274
+ */
275
+ static UploadBlobInfoFactory prefixObjectNames (String prefix ) {
276
+ return new PrefixObjectNames (prefix );
277
+ }
278
+
279
+ /** The default instance which applies not modification to the provided {@code fileName} */
280
+ static UploadBlobInfoFactory defaultInstance () {
281
+ return DefaultUploadBlobInfoFactory .INSTANCE ;
282
+ }
283
+
284
+ /**
285
+ * Convenience method to "lift" a {@link Function} that transforms the file name to an {@link
286
+ * UploadBlobInfoFactory}
287
+ */
288
+ static UploadBlobInfoFactory transformFileName (Function <String , String > fileNameTransformer ) {
289
+ return (b , f ) -> BlobInfo .newBuilder (b , fileNameTransformer .apply (f )).build ();
290
+ }
291
+ }
292
+
293
+ private static final class DefaultUploadBlobInfoFactory implements UploadBlobInfoFactory {
294
+ private static final DefaultUploadBlobInfoFactory INSTANCE = new DefaultUploadBlobInfoFactory ();
295
+
296
+ private DefaultUploadBlobInfoFactory () {}
297
+
298
+ @ Override
299
+ public BlobInfo apply (String bucketName , String fileName ) {
300
+ return BlobInfo .newBuilder (bucketName , fileName ).build ();
301
+ }
302
+ }
303
+
304
+ private static final class PrefixObjectNames implements UploadBlobInfoFactory {
305
+ private final String prefix ;
306
+
307
+ private PrefixObjectNames (String prefix ) {
308
+ this .prefix = prefix ;
309
+ }
310
+
311
+ @ Override
312
+ public BlobInfo apply (String bucketName , String fileName ) {
313
+ String separator = "" ;
314
+ if (!fileName .startsWith ("/" )) {
315
+ separator = "/" ;
316
+ }
317
+ return BlobInfo .newBuilder (bucketName , prefix + separator + fileName ).build ();
318
+ }
319
+
320
+ @ Override
321
+ public boolean equals (Object o ) {
322
+ if (this == o ) {
323
+ return true ;
324
+ }
325
+ if (!(o instanceof PrefixObjectNames )) {
326
+ return false ;
327
+ }
328
+ PrefixObjectNames that = (PrefixObjectNames ) o ;
329
+ return Objects .equals (prefix , that .prefix );
330
+ }
331
+
332
+ @ Override
333
+ public int hashCode () {
334
+ return Objects .hashCode (prefix );
335
+ }
336
+
337
+ @ Override
338
+ public String toString () {
339
+ return MoreObjects .toStringHelper (this ).add ("prefix" , prefix ).toString ();
206
340
}
207
341
}
208
342
}
0 commit comments