In my application I have a string which is a file content. I need to create new file with this content in blobstore. I tryed to use File API like this:
FileService fileService = FileServiceFactory.getFileService();
AppEngineFile file = fileService.createNewBlobFile("text/plain");
FileWriteChannel writeChannel = fileService.openWriteChannel(file, true);
writeChannel.write(ByteBuffer.wrap(content.getBytes()));
writeChannel.closeFinally();
BlobKey blobKey = fileService.getBlobKey(file);
res.sendRedirect("/serve?blob-key=" + blobKey);
But since the File API is deprecated I only get this error:
HTTP ERROR 500
Problem accessing /create_timetable. Reason:
The Files API is disabled. Further information: https://cloud.google.com/appengine/docs/deprecations/files_api
Caused by:
com.google.apphosting.api.ApiProxy$FeatureNotEnabledException: The Files API is disabled. Further information: https://cloud.google.com/appengine/docs/deprecations/files_api
at com.google.appengine.tools.development.ApiProxyLocalImpl$AsyncApiCall.callInternal(ApiProxyLocalImpl.java:515)
at com.google.appengine.tools.development.ApiProxyLocalImpl$AsyncApiCall.call(ApiProxyLocalImpl.java:484)
at com.google.appengine.tools.development.ApiProxyLocalImpl$AsyncApiCall.call(ApiProxyLocalImpl.java:461)
at java.util.concurrent.Executors$PrivilegedCallable$1.run(Executors.java:533)
at java.security.AccessController.doPrivileged(Native Method)
at java.util.concurrent.Executors$PrivilegedCallable.call(Executors.java:530)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
How can I manually create file in blobstore since user only gives me data content as string, not a file itself, so I can't use <form action="blobstoreService.createUploadUrl("/upload") %>" and <input type="file">
Update
As #ozarov suggested I did it using Google Cloude Storage API and wrote the function below. It also returns BlobKey of this file so you can access it using Blobstore API.
private BlobKey saveFile(String gcsBucket, String fileName,
String content) throws IOException {
GcsFilename gcsFileName = new GcsFilename(gcsBucket, fileName);
GcsOutputChannel outputChannel =
gcsService.createOrReplace(gcsFileName, GcsFileOptions.getDefaultInstance());
outputChannel.write(ByteBuffer.wrap(content.getBytes()));
outputChannel.close();
return blobstoreService.createGsBlobKey(
"/gs/" + gcsFileName.getBucketName() + "/" + gcsFileName.getObjectName());
}
You should write to Google Cloud Storage instead.
Files API is deprecated and you are correct that the Blobstore API
does not provide a programmatic way to write directly to it.
Later you can read directly from Google Cloud Storage using its own API or
you can also use the Blobstore API to do so by creating a BlobKey for it.
Related
This is my first time using amazon s3 and I want to store pdf files that I create using itext in java spring.
The code (hosted on ec2 instance) creates a pdf that I would like to store somewhere. I am exploring if amazon s3 can hold those files. Eventually I would like to retrieve it as well. Can this be done using itext and java spring? Any examples would be great.
To Upload Files to Amazon s3 You need to use putObject method of AmazonS3Client class like this:
AWSCredentials credentials = new BasicAWSCredentials(appId,appSecret);
AmazonS3 s3Client = new AmazonS3Client(credentials);
String bucketPath = "YOUR_BUCKET_NAME/FOLDER_INSIDE_BUCKET";
InputStream is = new FileInputStream("YOUR_PDF_FILE_PATH");
ObjectMetadata meta = new ObjectMetadata();
meta.setContentLength(is.available());
s3Client.putObject(new PutObjectRequest(bucketPath,"YOUR_FILE.pdf", is, meta).withCannedAcl(CannedAccessControlList.Private));
And to get file from S3, You need to generate a pre-signed URL to access private file from S3 or if your files are public then you can directly access your file by hitting link of file in your browser, The link for your file will be available in AWS S3 console.
Also we have specified CannedAccessControlList.Private in the above upload code which means we are making permission of file as private So we need to generate presigned URL to access file like this:
AWSCredentials credentials = new BasicAWSCredentials(appId,appSecret);
AmazonS3 s3Client = new AmazonS3Client(credentials);
GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest("YOUR_BUCKET_NAME", "FOLDER_INSIDE_BUCKET/YOUR_FILE.pdf");
generatePresignedUrlRequest.setMethod(HttpMethod.GET);
Date expiration = new Date();
long milliSeconds = expiration.getTime();
milliSeconds += 1000 * 60 * 60; // Add 1 hour.
expiration.setTime(milliSeconds);
generatePresignedUrlRequest.setExpiration(expiration);
URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);
String finalUrl = url.toString();
I have an SPI Google endpoint, I can't find any examples on which annotation to use to accept a for example a MultipartFile file?
#ApiMethod(name = "saveNewBill" ,
httpMethod = ApiMethod.HttpMethod.POST)
public Bill saveBillImage( #Named("content") MultipartContent f ){
Bill bill = new Bill();
return bill;
}
EDIT: I have just noticed, while reading up on some other things Blobstore related, that Google now recommends using Google Cloud Storage INSTEAD of the Blobstore for serving of media.
Since you are using an Endpoint on App Engine, you should use the BlobStore.
It is the preferred way to handle uploading, storage and retrieval of images on App Engine.
The following function in the BlobstoreService will generate an upload URL which you then upload the image to using a standard Multipart Request with the image data being passed in a parameter named file.
BlobstoreServiceFactory.getBlobstoreService().createUploadUrl("/[servlet name goes here]");
You will need to provide the name of a Servlet to which the request will be redirected after the upload has completed. This Servlet can access the newly created Blobstore item and do something meaningful with it - such as get the serving URL of the image and return it to the client.
Here is a snippet of a Servlet that does just that:
public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
List<BlobKey> blobs = blobstoreService.getUploads(req).get("file");
BlobKey blobKey = blobs.get(0);
ImagesService imagesService = ImagesServiceFactory.getImagesService();
ServingUrlOptions servingOptions = ServingUrlOptions.Builder.withBlobKey(blobKey);
servingOptions.secureUrl(true);
String servingUrl = imagesService.getServingUrl(servingOptions);
res.setStatus(HttpServletResponse.SC_OK);
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
out.print(servingUrl);
out.flush();
out.close();
}
You can also add some querystring parameters of your own to the upload URL which can be read in the Servlet. This can be useful for things like attaching the BlobKey to a specific entity etc.
For a more in-depth look at the Blobstore, I recommend the following article from Romin Irani's App Engine Tutorial: https://rominirani.com/episode-13-using-the-blobstore-java-api-56423cf6a1b#.6t95vziul
I create data in the server (gae) and I want to store it in Blobstore. I saw many answers on how to do this giving a BlobStore URL to the client, but there is no client or HTTP request: it's just an asynchronous task.
Then I guess I should use createUploadUrl(), and instead of giving this URL to a client, from my code HTTP Post my data to it via URL Fetch. This looks weird, isn't there another API for this?
Let's say that the files I want in Blobstore are already stored in my GCS default bucket. Can I just tell Blobstore about them using the gcs location "/gs/bucketname/file"? I tried this by
GcsFilename filename = new GcsFilename(bucketName, fileId);
String gcsKey = "/gs/" + bucketName + "/" + filename.getObjectName();
BlobKey blobKey = blobstoreService.createGsBlobKey(gcsKey);
GcsOutputChannel outputChannel = gcsService.createOrReplace(filename, GcsFileOptions.getDefaultInstance());
ObjectOutputStream oout = new ObjectOutputStream(Channels.newOutputStream(outputChannel));
oout.writeObject(myDataObjectToPersist);
oout.close();
// ...at some other point I have checked the file is correctly stored in
// GCS and I can fetch it using /gs/bucket/fileId
// but it doesn't seem to be in Blobstore, so when
InputStream stream = new BlobstoreInputStream(new BlobKey(blobKey.keyString))
// ... this gives a BlobstoreInputStream$BlobstoreIOException: BlobstoreInputStream received an invalid blob key...
Is this something conceptually wrong - like if I use GcsOutputChannel to save it I will not get it from Blobstore even if I create a BlobKey, or is it something that could work but I just did something wrong?
1K thanks
Why would you want to store the file in blobstore as opposed to writing and reading it directly from GCS?
Yes, you can create a BlobKey for a file stored in GCS, and can use the key in some of the blobstore API (such as fetchData and serve) but unfortunately not in all.
Some of the blobstore API (such as BlobstoreInputStream) depends on BlobInfo and that is not created when using the GCS client.
I have some file upload code that works on appspot.com but not on local Java dev server.
I am uploading to Google Cloud Storage, via:
String bucketName = AppIdentityServiceFactory.getAppIdentityService().getDefaultGcsBucketName();
String uploadUrl = blobstoreService.createUploadUrl(
successUrl,
UploadOptions.Builder.withGoogleStorageBucketName(bucketName)
);
in my upload handler (pointed to by "successUrl"), I try and read the newly uploaded blob from the GCS bucket. This is done like this:
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
Map<String, List<BlobKey>> uploads = blobstoreService.getUploads(req);
Map<String, List<FileInfo>> files = blobstoreService.getFileInfos(req);
...
// use the FileInfo instance to do fileInfo.getGsObjectName()
...
GcsInputChannel readChannel = gcsService.openPrefetchingReadChannel(gcsFilename, 0, 1024 * 1024);
InputStream is = Channels.newInputStream(readChannel));
up on the actual appengine instance, fileInfo.getGsObjectName() returns a working path like this:
/gs/myapp.appspot.com/L2F..LongIDHere...YmJV
which works and can be read, BUT, on the local dev server, I get something like:
/gs/app_default_bucket/fake-encoded_gs_key:YXB...LongIdHere...6de
which throws FileNotFoundException when tried read using the same code, that works on appEngine :-(
I'm struggling for few days with this problem and you are my last chance solving it.
The Goal:
To upload a bitmap from android client to google app engine and save it in datastore.
I'm using Serialization to transfer object from client to server and vise versa.
Things I have tried:
sending a Bitmap
but i got java.io.NotSerializableException: android.graphics.Bitmap
then I tried create from the Bitmap Blob at the client like this:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
Byte[] bArray = bos.toByteArray();
Blob blob = new Blob(bArray);
But at the client side I dont have the google app engine classes (for Blob), So I tried to brute force it by extract specific classess but it created new problems.
So I am asking in which dierction I can go from to solve this problem.
Thanks.
Few notes:
Do not use Java serialization to transfer data between JVMs. Java serialization is not standardized and is not guaranteed to be compatible between JVMs (or even between versions).
To send binary data it's best to use HTTP POST and set Content-Type appropriately (e.g. application/octet-stream).
So, to make this work do this:
Create a servlet which handles POST and gets the binary data. Use servletRequest.getInputStream() to get hold of binary data.
Use Blobstore FileService API to save data to blobstore.
On Android side use a http client to make a POST request and add your bitmap's binary data to it. If you need to add some metadata use Http headers.
This might be useful
How to upload and store an image with google app engine (java)
Alternatively, you can try blobstore api
http://code.google.com/appengine/docs/java/blobstore/overview.html
Here is a production tested way:
Use GAE appengine to upload your bitmap to, and serve for future clients.
On the Android code, follow these steps:
Get an Upload URL from GAE
Upload your bitmap to GAE, and get a blobkey back
Later on, use the blobkey to serve the image to your clients.
GAE Servlet code:
getUploadURL:
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
String url = blobstoreService.createUploadUrl(path_to_your_upload_servlet);
uploadServlet - stores in blobstore, returns the blobkey to the uploader
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
Map<String, List<BlobKey>> uploads = blobstoreService.getUploads(request);
String fileName = uploads.keySet().iterator().next();
final BlobKey blobKey = uploads.get(fileName).get(0);
response.getWriter().println(blobKey.getKeyString());
Android client code:
String uploadUrl = getUrlAsString(..your getUrl servlet path...)
// Upload to GAE (include apache-mime4j.jar and httpmime.jar in your project for this code)
File file = new File(imageFilePath);
HttpPost postRequest = new HttpPost(uploadUrl);
MultipartEntity entity = new MultipartEntity();
entity.addPart("file", new FileBody(file));
postRequest.setEntity(entity);
HttpResponse httpResponse;
HttpClient httpClient = new DefaultHttpClient();
httpClient.getParams().setBooleanParameter("http.protocol.handle-redirects",false);
httpResponse = httpClient.execute(postRequest);
int status = httpResponse.getStatusLine().getStatusCode();
String blobKey = getInputStreamAsString(httpResponse.getEntity().getContent())