-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: auto content-type on blob creation #338
Changes from 3 commits
9795972
a5da742
6320364
3cc79cb
15fe56c
6b7f98b
6b14a3e
736ed2e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,6 +81,7 @@ | |
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import javax.activation.MimetypesFileTypeMap; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use com.google.common.net.MediaType so you don't introduce another dependency, and ping the guava team to get this out of beta There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. com.google.common.net.MediaType doesn't provide mapping extension to the meida type. Its parse method recognizes strings like "text/plain". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about Files.probeContentType(Path)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or URLConnection There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both Files.probeContentType(Path) and URLConnection work! I will use it instead. Thanks for the hint. |
||
|
||
public class HttpStorageRpc implements StorageRpc { | ||
public static final String DEFAULT_PROJECTION = "full"; | ||
|
@@ -98,6 +99,7 @@ public class HttpStorageRpc implements StorageRpc { | |
private final HttpRequestInitializer batchRequestInitializer; | ||
|
||
private static final long MEGABYTE = 1024L * 1024L; | ||
private static final MimetypesFileTypeMap MIMETYPES_FILE_TYPE_MAP = new MimetypesFileTypeMap(); | ||
|
||
public HttpStorageRpc(StorageOptions options) { | ||
HttpTransportOptions transportOptions = (HttpTransportOptions) options.getTransportOptions(); | ||
|
@@ -286,7 +288,7 @@ public StorageObject create( | |
.insert( | ||
storageObject.getBucket(), | ||
storageObject, | ||
new InputStreamContent(storageObject.getContentType(), content)); | ||
new InputStreamContent(detectContentType(storageObject), content)); | ||
insert.getMediaHttpUploader().setDirectUploadEnabled(true); | ||
Boolean disableGzipContent = Option.IF_DISABLE_GZIP_CONTENT.getBoolean(options); | ||
if (disableGzipContent != null) { | ||
|
@@ -372,6 +374,13 @@ public Tuple<String, Iterable<StorageObject>> list(final String bucket, Map<Opti | |
} | ||
} | ||
|
||
private static String detectContentType(StorageObject object) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be an optional feature that's disabled by default. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking about making this feature optional, but I didn't find an appropriate place to store this setting. It could be a property of a project/client/bucket/object. I only found: https://cloud.google.com/storage/docs/metadata#content-type If I read this correctly, the Storage should attempt to determine the content type if not explicitly set, so this feature should be on by default. BTW, right now the content type of blobs created with Storage.create() methods is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Storage doesn't provide contentType detection in the API. If contentType is set at creation then Please make this optional otherwise it's a behavior change not originally intended on uploads with the Java client. |
||
String contentType = object.getContentType(); | ||
return contentType != null | ||
? contentType | ||
: MIMETYPES_FILE_TYPE_MAP.getContentType(object.getName().toLowerCase()); | ||
} | ||
|
||
private static Function<String, StorageObject> objectFromPrefix(final String bucket) { | ||
return new Function<String, StorageObject>() { | ||
@Override | ||
|
@@ -811,9 +820,7 @@ public String open(StorageObject object, Map<Option, ?> options) { | |
HttpRequest httpRequest = | ||
requestFactory.buildPostRequest(url, new JsonHttpContent(jsonFactory, object)); | ||
HttpHeaders requestHeaders = httpRequest.getHeaders(); | ||
requestHeaders.set( | ||
"X-Upload-Content-Type", | ||
firstNonNull(object.getContentType(), "application/octet-stream")); | ||
requestHeaders.set("X-Upload-Content-Type", detectContentType(object)); | ||
String key = Option.CUSTOMER_SUPPLIED_KEY.getString(options); | ||
if (key != null) { | ||
BaseEncoding base64 = BaseEncoding.base64(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3273,4 +3273,45 @@ public void testBlobReload() throws Exception { | |
updated.delete(); | ||
assertNull(updated.reload()); | ||
} | ||
|
||
private Blob createBlob(String method, BlobInfo blobInfo) throws IOException { | ||
switch (method) { | ||
case "create": | ||
return storage.create(blobInfo); | ||
case "writer": | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why the braces here? |
||
storage.writer(blobInfo).close(); | ||
return storage.get(BlobId.of(blobInfo.getBucket(), blobInfo.getName())); | ||
} | ||
default: | ||
throw new IllegalArgumentException("Unknown method " + method); | ||
} | ||
} | ||
|
||
private void testAutoContentType(String method) throws IOException { | ||
String[] names = {"file1.txt", "dir with spaces/Pic.Jpg", "no_extension"}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a way to infer the type based on bytes rather than file extension? That's how Golang does it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not always possible. In case of resumeable upload a blob is created first and the content is available later:
I did an experiment: gsutil: text/plain To achieve consistent behavior across all the clients it should be implemented on the server-side at the moment when the upload is finished. Until this feature is implemented I suggest type detecting based on file extension, as it has been done recently in nodejs (googleapis/nodejs-storage#1190) |
||
String[] types = {"text/plain", "image/jpeg", "application/octet-stream"}; | ||
Blob blob = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can push this into the for loop |
||
for (int i = 0; i < names.length; i++) { | ||
BlobId blobId = BlobId.of(BUCKET, names[i]); | ||
BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build(); | ||
blob = createBlob(method, blobInfo); | ||
assertEquals(types[i], blob.getContentType()); | ||
} | ||
String customType = "custom/type"; | ||
BlobId blobId = BlobId.of(BUCKET, names[0]); | ||
BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType(customType).build(); | ||
blob = createBlob(method, blobInfo); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't reuse local variable. There are two separate blob objects here that coincidentally share a variable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had a reason to reuse the variable but after rewriting the test that reason is not actual anymore. Thanks for catching that. |
||
assertEquals(customType, blob.getContentType()); | ||
} | ||
|
||
@Test | ||
public void testAutoContentTypeCreate() throws IOException { | ||
testAutoContentType("create"); | ||
} | ||
|
||
@Test | ||
public void testAutoContentTypeWriter() throws IOException { | ||
testAutoContentType("writer"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this included in java 6 and later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it works with jdk7,8. This dependency is required for java9+