I'm trying to rotate / flip an image that is already sitting inside Google Cloud Storage.
This is the blobkey:
BlobKey blobKey = new BlobKey(upload.getBlobKey());
Then I retrieve the image and apply an image transform:
ImagesService imagesService = ImagesServiceFactory.getImagesService();
Image image = ImagesServiceFactory.makeImageFromBlob(blobKey);
Transform transform = ImagesServiceFactory.makeRotate(90);
Image newImage = imagesService.applyTransform(transform, image);
The RAW image data I can get in a byte array using:
newImage.getImageData()
Using GCS directly, I can write it to the cloud storage service:
GcsFilename fileName = new GcsFilename("bucket-name", upload.getFileName());
GcsOutputChannel outputChannel = GcsServiceFactory.createGcsService().createOrReplace(fileName, GcsFileOptions.getDefaultInstance());
outputChannel.write(ByteBuffer.wrap(newImage.getImageData()));
outputChannel.close();
Locally I can see the rotated image sitting inside the appengine-generated folder, how do I get the new serving url or what should the objectName be in order to overwrite the original image? Is the objectName the same as the original filename when it was uploaded, is it the blobkey, ...?
Update:
I'm storing the GcsFilename upon upload:
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
Map<String,List<FileInfo>> finfos = blobstoreService.getFileInfos(req);
String gcsFileName = finfos.get("uploadKey").get(0).getGsObjectName();
upload.setGcsFileName(gcsFileName);
upload.persist();
The GcsFileName looks like this (locally):
/gs/bucket-name/fake-encoded_gs_key:YXVjdGlvbi1wb3J0YWwtdXBsb2Fkcy84SXp5VGpIUWlZSDY5X1Ytck5TbGtR-0f969f5b4f53a01479ff2d5eaf02fa1a
(unindexed)
Then inside my rotate method, I do the following:
GcsFilename fileName = new GcsFilename( "bucket-name", upload.getGcsFileName());
GcsOutputChannel outputChannel = GcsServiceFactory.createGcsService().createOrReplace(fileName, GcsFileOptions.getDefaultInstance());
outputChannel.write(ByteBuffer.wrap(newImage.getImageData()));
outputChannel.close();
BlobstoreService bs = BlobstoreServiceFactory.getBlobstoreService();
BlobKey bk = bs.createGsBlobKey(fileName.getObjectName());
When I print the new blobKey using bk.getKeyString(), I get an enormous blobkey:
encoded_gs_key:L2dzL2F1Y3Rpb24tcG9ydGFsLXVwbG9hZHMvZmFrZS1lbmNvZGVkX2dzX2tleTpZWFZqZEdsdmJpMXdiM0owWVd3dGRYQnNiMkZrY3k4NFNYcDVWR3BJVVdsWlNEWTVYMVl0Y2s1VGJHdFItMGY5NjlmNWI0ZjUzYTAxNDc5ZmYyZDVlYWYwMmZhMWE
When I try:
imagesService.getServingUrl(ServingUrlOptions.Builder.withBlobKey(bk);
I now get a INVALID_BLOB_KEY: Could not read blob.
Same with:
imagesService.getServingUrl(ServingUrlOptions.Builder.withGoogleStorageFileName(fileName.getObjectName()));
INVALID_BLOB_KEY: Could not read blob.
Update - Testing On Live:
I've done some logging on live to see if I can pinpoint where things are going wrong.
When I print out the GcsFilename, this is what I'm getting:
GcsFilename fileName = new GcsFilename("bucket-name", upload.getGcsFileName());
System.out.println("fileName.getObjectName(): " + fileName.getObjectName());
System.out.println("fileName.getBucketName(): " + fileName.getBucketName());
2014-03-08 09:31:35.076 [s~_____/live-1-0-2.374269833496060124].<stdout>: fileName.getObjectName(): /gs/bucket-name/L2FwcGhvc3RpbmdfcHJvZC9ibG9icy9BRW5CMlI
2014-03-08 09:31:35.076 [s~_____/live-1-0-2.374269833496060124].<stdout>: fileName.getBucketName(): bucket-name
Then generating the new blobkey:
BlobstoreService bs = BlobstoreServiceFactory.getBlobstoreService();
BlobKey bk = bs.createGsBlobKey(fileName.getObjectName());
System.out.println("GSBlobKey: " + bk.getKeyString());
2014-03-08 09:31:37.993 [s~_____/live-1-0-2.374269833496060124].<stdout>: GSBlobKey: AMIfv94Tj3xO9hPWwMm314Id0obQs1gVWOHYVuGaaAjjMbieBUD6x0nNGbqiY8o5Jk9iLga4pPe9-8L1
Then using 3 different methods for getting the new serving urls:
String newImageUrl1 = imagesService.getServingUrl(ServingUrlOptions.Builder.withGoogleStorageFileName(fileName.getObjectName()));
System.out.println("newImageUrl1: " + newImageUrl1);
String newImageUrl2 = imagesService.getServingUrl(ServingUrlOptions.Builder.withBlobKey(bk));
System.out.println("newImageUrl2: " + newImageUrl2);
String newImageUrl3 = imagesService.getServingUrl(bk);
System.out.println("newImageUrl3: " + newImageUrl3);
2014-03-08 09:31:38.116 [s~_____/live-1-0-2.374269833496060124].<stdout>: newImageUrl1: http://lh3.ggpht.com/My7zTT79xOltI3cMzHidx7-Us91Qlp13jdv-vJceQ_OpX3FONSr9YzI2...
2014-03-08 09:31:38.152 [s~_____/live-1-0-2.374269833496060124].<stdout>: newImageUrl2: http://lh3.ggpht.com/My7zTT79xOltI3cMzHidx7-Us91Qlp13jdv-vJceQ_OpX3FONSr9YzI2...
2014-03-08 09:31:38.218 [s~_____/live-1-0-2.374269833496060124].<stdout>: newImageUrl3: http://lh3.ggpht.com/My7zTT79xOltI3cMzHidx7-Us91Qlp13jdv-vJceQ_OpX3FONSr9YzI2...
When I access these URLs, all I'm getting is the original image.
When I look in the Cloud Storage Dashboard, the original image is sitting in:
Home/bucket-name/
The rotated image however is sitting in:
Home/bucket-name//gs/bucket-name/
Hence the reason it's not overwriting the original.
I'm guessing either the fileName I'm creating is incorrect or the GcsOutputChannel I'm creating is changing the path?
I'm also curious why I'm getting the original image's url instead of the new image's url?
If I split the original GcsFileName and only take the last part, I'm getting an error:
GcsFilename fileName = new GcsFilename("bucket-name", upload.getGcsFileName().split("/")[3]);
Google storage filenames must be prefixed with /gs/
Any ideas?
Solution
Retreive blobKey using:
BlobKey blobKey = blobstoreService.createGsBlobKey("/gs/" + fileName.getBucketName() + "/" + fileName.getObjectName());
... even though fileName.getObjectName() already contains "/gs/bucket-name/" in front of the objectname. When printing it to standard out, your fileName should contain /gs/bucket-name/ twice, weird, but it works.
Using that blobKey, the correct serving URL is created
See Question&Answers more detail:
os