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
Related
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 wrote a simple SpringMVC app and host on a Paas. I have created a table in Mysql and a column is the Blob. I can upload files through the Mysql admin. Right now, my server can serve html file or javascript files correctly in browser. However, when I serve a jpg file in http://myserver.com/File/ad.jpg, my browser showed a small icon and if I save it, the Windows Image software shows that the image is damaged.
Here are some of the code:
#RequestMapping(value="/File/**", //{name:.+}",
method = RequestMethod.GET)
public #ResponseBody void getContent(
// #PathVariable("name") String name,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
String name = request.getPathInfo();
....
IOUtils.copy(blob.getBinaryStream(), out);
I found that getServletContext() returns null, so I wasn't able to get contentType, so I saved contentType in Mysql as image/jpeg for the ad.jpg. I set the disposition to be inline. What else should I do to serve a jpg?
I finally found that the original code has nothing wrong. The original Mysql admin web page uploaded the blob incorrectly. After I found the Paas has a secret new admin page and that can upload a correct blob with an binary option. I still appreciate all the replies in comments.
I have an app that allows users to save blobs in the blobstore. I have a schema that does so presently, but I am interested in something simpler and less twisted. For context, imagine my app allows users to upload the picture of an animal with a paragraph describing what the animal is doing.
Present schema
User calls my endpoint api to save the paragraph and name of the animal in entity Animal. Note: The Animal entity actually has 4 fields ( name, paragraph, BlobKey, and blobServingUrl as String). But the endpoint api only allows saving of the two mentioned.
Within the endpoint method, on app-engine side, after saving name and paragraph I make the following call to generate a blob serving url, which my endpoint method returns to the caller
#ApiMethod(name = "saveAnimalData", httpMethod = HttpMethod.POST)
public String saveAnimalData(AnimalData request) throws Exception {
...
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
String url = blobstoreService.createUploadUrl("/upload");
return url;
}
On the android side, I use a normal http call to send the byte[] of the image to the blobstore. I use apache DefaultHttpClient(). Note: the blobstore, after saving the image, calls my app-engine server with the blob key and serving url
I read the response from the blobstore (blobstore called my callback url) using a normal java servlet, i.e. public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException. From the servlet, I put the BlobKey and blobServingUrl into the Animal entity for the associated animal. (I had passed some meta data to the blobstore, which I use as markers to identify the associated animal entity).
Desired Schema
This is where your response comes in. Essential, I would like to eliminate the java servlet and have my entire api restricted to google cloud endpoint. So my question is: how would I use my endpoint to execute steps 3 and 4?
So the idea would be to send the image bytes to the endpoint method saveAnimalData at the same time that I am sending the paragraph and name data. And then within the endpoint method, send the image to the blobstore and then persist the BlobKey and blobServingUrl in my entity Animal.
Your response must be in java. Thanks.
I see two questions in one here :
Can Google Cloud Endpoints handle multipart files ? -> I don't know about this TBH
Is there a simpler process to store blobs than using the BlobStoreService?
It depends on the size of your image. If you limit your users to < 1MB files, you could just store your image as a Blob property of your Animal entity. It would allow you to bypass the BlobStoreService plumbering. See : https://developers.google.com/appengine/docs/java/datastore/entities?hl=FR
This solution still depends on how the Cloud Endpoint would handle the multipart file as a raw byte[]...
We encountered the same issue with GWT + Google App Engine in 2009, and it was before the BlobStoreService was made available.
GWT RPC and Cloud Endpoints interfaces share some similarities, and for us it was not possible. We had to create a plain HTTP Servlet, and use a Streaming Multipart file resolver beacause the one from Apache's HTTP Commons used the file system.
Here is my Serve.java, which is my Serve servlet.
public class Serve extends HttpServlet {
private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {
BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
blobstoreService.serve(blobKey, res);
}
}
When I host my server and access serve servlet via browser, I see my blob (a picture). So I'm assuming the blob, in its picture file format, is stored in HttpServletResponse.
How do I access this blob / file? I actually need the file from an Android app using HttpResponse, but I'll figure that out myself.
You can create a BlobstoreInputStream
to read a blob from the BlobStore programmatically.
If you need the file in an Android app, should serve it the way you already do and just read the response in your app.
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())