File Upload google app engine blobstore - google-app-engine

I am going to use Google app engine Blobstore to store my uploaded files. File type can be anything (.txt,.pdf,.docx etc)
I have written following servlet to download stored files in Google app engine Blobstore.
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);
}
}
But every time I request a file by giving a url as below,
http://127.0.0.1:8888/serve?blob-key=DEHQ3U_2wtUdEL7XPI434Q
file is downloaded nicely. But no extension for file. And name of the file is always ‘serve’
What should I do to download the uploaded file with the original name of the file?
Thanks,

I used the following code to serve a blob by passing the blob-key as a String to my Serve.java . File is downloaded using the original filename as well as the original extension. Found a more detailed discussion at http://onjava.com/onjava/excerpt/jebp_3/index3.html
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
BlobInfoFactory bi = new BlobInfoFactory();
BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
String fname = bi.loadBlobInfo(blobKey).getFilename();
res.setContentType("application/x-download");
res.setHeader("Content-Disposition", "attachment; filename=" + fname);
blobstoreService.serve(blobKey, res);

I know python has a send_as option that automatically sets the filename for the response, but I think in Java you will have to add a Content-Disposition header to the response like:
Content-Disposition: attachment; filename=your-file.docx;
The BlobInfo for your blob keeps the original filename and you can fetch it via getFilename

Related

How to upload pdf files in Tomcat directory?

I am using Jersey for my REST backend, and i am developing a pdf upload feature. I prepared HTML form and backend REST method to upload, but i am able to upload only in my PC drive (example, on path C:/uploaded). Is there a method to upload in a Tomcat directory outside my app? For example, under "webapps/my-files"?
I am using Jersey 2.3 with multipart library and Tomcat 8.5.37 if needed.
I read lots of toturials and other questions, but all of then use c:/uploadedFiles or similar as upload location, so my doubts are still there.
First place, I wrote my REST service:
#POST
#Path("upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.TEXT_PLAIN)
public String uploadFile (#FormDataParam("file") InputStream file, #FormDataParam("file") FormDataContentDisposition fileData) {
String uploadPath = "C:\\uploaded" + fileData.getFileName();
try {
int read = 0;
byte[] bytes = new byte[1024];
OutputStream out = new FileOutputStream(new File (uploadPath));
while ((read = file.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.flush();
out.close();
return "Completed";
} catch (IOException ex) {
return ex.getMessage();
}
}
Then, i tried to call it from Postman with POST method and using a simple pdf file as request body, and it works.
But now I want to upload in a server folder and not in any C:/ drive location. I want to know, where should I put the directory on server? I tried under webapps/, is ok? And then, how can I set variable "uploadPath" correctly to upload in that directory?

Wildcards in GCS bucket Java client api

Using Wildcards in file name i am trying to read files from GCS bucket.
in gsutil command line wildcards is working in specifying file names.
but in java client api
GcsFilename filename = new GcsFilename(BUCKETNAME, "big*");
it is searching for file named "big*" instead of file starting with big .
please help me how i can use Wildcards in GCSFilename.
Thanks in advance.
Wildcard characters are a feature of gsutil, but they're not an inherent part of the Google Cloud Storage API. You can, however, handle this the same way that gsutil does.
If you want to find the name of every object that begins with a certain prefix, Google Cloud Storage's APIs provide a list method with a "prefix" argument. Only objects matching the prefix will be returned. This doesn't work for arbitrary regular expressions, but it will work for your example.
The documentation for the list method goes into more detail.
As Brandon Yarbrough mentioned, GcsFilename represent a name of a single GCS Object, which could include any valid UTF-8 character [excluding a few such as \r \n but including '*' though
not recommended). see https://developers.google.com/storage/docs/bucketnaming#objectnames for more info.
GAE GCS client does not support listing yet (though that is planned to be added), so for now you can use the GCS XML or JSON API directly (using urlfetch) or use the Java GCS api client, https://developers.google.com/api-client-library/java/apis/storage/v1
See example for the latter option:
public class ListServlet extends HttpServlet {
public static final List<String> OAUTH_SCOPES =
ImmutableList.of("https://www.googleapis.com/auth/devstorage.read_write");
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
String bucket = req.getParameter("bucket");
AppIdentityCredential cred = new AppIdentityCredential(OAUTH_SCOPES);
Storage storage = new Storage.Builder(new UrlFetchTransport(), new JacksonFactory(), cred)
.setApplicationName(SystemProperty.applicationId.get()).build();
Objects.List list = storage.objects().list(bucket);
for (StorageObject o : list.execute().getItems()) {
resp.getWriter().println(o.getName() + " -> " + o);
}
} catch (Exception ex) {
throw new ServletException(ex);
}
}
}

Converting com.google.api.services.drive.model.File to java.io.File

So I want to create a java.io.File so that I can use it to generate a multipart-form POST request. I have the file in the form of a com.google.api.services.drive.model.File so I'm wondering, is there a way I can convert this Google File to a Java File? This is a web-app that uses the Google App Engine SDK, which prohibits every approach I've tried to make this work
No, you it doesn't seem like you can convert from com.google.api.services.drive.model.File to java.io.File. But it should still be possible to generate a multipart-form POST request using your data in Drive.
So the com.google.api.services.drive.model.File class is used for storing metadata about the file. It's not storing the file contents.
If you want to read the contents of your file into memory, this code snippet from the Drive documentation shows how to do it. Once the file is in memory, you can do whatever you want with it.
/**
* Download the content of the given file.
*
* #param service Drive service to use for downloading.
* #param file File metadata object whose content to download.
* #return String representation of file content. String is returned here
* because this app is setup for text/plain files.
* #throws IOException Thrown if the request fails for whatever reason.
*/
private String downloadFileContent(Drive service, File file)
throws IOException {
GenericUrl url = new GenericUrl(file.getDownloadUrl());
HttpResponse response = service.getRequestFactory().buildGetRequest(url)
.execute();
try {
return new Scanner(response.getContent()).useDelimiter("\\A").next();
} catch (java.util.NoSuchElementException e) {
return "";
}
}
https://developers.google.com/drive/examples/java
This post might be helpful for making your multi-part POST request from Google AppEngine.
In GoogleDrive Api v3 you can download the file content into your OutputStream. You need for that the file id, which you can get from your com.google.api.services.drive.model.File:
String fileId = "yourFileId";
OutputStream outputStream = new ByteArrayOutputStream();
driveService.files().get(fileId).executeMediaAndDownloadTo(outputStream);

which are the files uri on GAE java emulating cloud storage with GCS client library?

I'm developing a web application using Google app engine for Java.
I will use Google Cloud storage and according to the documentation, I'm using GCS client library to emulate cloud storage on local disk.
I have no problem saving the files, I can see them from eclipse under the war folder (under the path WEB-INF/appengine-generated) and I can see them from the web admin panel accessible from the url
localhost:8888/_ah/admin
as indicated in this question
My question is the following. Which are the files URI under localhost to access them with GCS emulation?
Example of one of uploaded files on localhost:
file key is aglub19hcHBfaWRyJwsSF19haF9GYWtlQ2xvdWRTdG9yYWdlX18xIgpxcmNvZGUuanBnDA
ID/name is encoded_gs_key:L2dzLzEvcXJjb2RlLmpwZw
filename is /gs/1/qrcode.jpg
Thanks in advance.
You can see how this is done here:
https://code.google.com/p/appengine-gcs-client/source/browse/trunk/java/src/main/java/com/google/appengine/tools/cloudstorage/dev/LocalRawGcsService.java
As of today this mapping is being maintained by the using the local datastore. This may change in the future, but you should be able to simply call into this class or one of the higher level classes provided with the GCS client to get at the data.
Using getServingUrl()
The local gcs file is saved into a blob format.
When saving it, I can use location like your filename "/gs/1/qrcode.jpg"
Yet, when accessing it, this fake location is not working.
I found a way. It may not be the best, but works for me.
BlobKey bk = BlobstoreServiceFactory.getBlobstoreService().createGsBlobKey(location);
String url = ImagesServiceFactory.getImagesService().getServingUrl(bk);
The url will be like:
http://127.0.0.1:8080/_ah/img/encoded_gs_key:yourkey
(I was hardly to find any direct solution by google search.
I hope this answer can help others in need.)
Resource: ImagesServiceFactory ImageService
FileServiceFactory
For those who wish to serve the local GCS files that have been created by the GAE GCS library, one solution is to expose a Java Servlet like this:
package my.applicaion.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
public final class GoogleCloudStorageServlet
extends HttpServlet
{
#Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException
{
final BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
final String fileName = "/gs" + request.getPathInfo();
final BlobKey blobKey = blobstoreService.createGsBlobKey(fileName);
blobstoreService.serve(blobKey, response);
}
}
and in your web.xml:
<servlet>
<servlet-name>GoogleCloudStorage</servlet-name>
<servlet-class>my.applicaion.servlet.GoogleCloudStorageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GoogleCloudStorage</servlet-name>
<url-pattern>/gcs/*</url-pattern>
</servlet-mapping>
If you host this servlet in your GAE application, the URL for accessing a GCS file with bucket bucket-name and with name fileName is http://localhost:8181:/gcs/bucket-name/fileName, the local GAE development server port number being 8181.
This works at least from GAE v1.9.50.
And if you intend to have the local GCS server working in a unit test with Jetty, here is a work-around, hopefully with the right comments:
final int localGcsPortNumber = 8081;
final Server localGcsServer = new Server(localGcsPortNumber);
final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
final String allPathSpec = "/*";
context.addServlet(new ServletHolder(new HttpServlet()
{
#Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
final BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
final String fileName = "/gs" + request.getRequestURI();
final BlobKey blobKey = blobstoreService.createGsBlobKey(fileName);
if (blobKey != null)
{
// This is a work-around over the "ServeBlobFilter" which does not take the "Content-Type" from the "blobInfo", but attempts to retrieve it from the "blobKey"
final BlobInfo blobInfo = BlobStorageFactory.getBlobInfoStorage().loadGsFileInfo(blobKey);
if (blobInfo != null)
{
final String contentType = blobInfo.getContentType();
if (contentType != null)
{
response.addHeader(HttpHeaders.CONTENT_TYPE, contentType);
}
}
}
blobstoreService.serve(blobKey, response);
}
}), allPathSpec);
// The filter is responsible for taken the "blobKey" from the HTTP header and for fulfilling the response with the corresponding GCS content
context.addFilter(ServeBlobFilter.class, allPathSpec, EnumSet.of(DispatcherType.REQUEST));
// This attribute must be set, otherwise a "NullPointerException" is thrown
context.getServletContext().setAttribute("com.google.appengine.devappserver.ApiProxyLocal", LocalServiceTestHelper.getApiProxyLocal());
localGcsServer.setHandler(context);
localGcsServer.start();

GWT: Get URL of file located on server

I am developing an web application which can upload/download a file from client to PostgreSQL database server via GWT RPC call.
I managed to create an upload servlet which store desired file(choosed by user via FileUpload widget) to Glassfish "TEMP" directory => then i used SQL command:
INSERT INTO table VALUES ('"+name+"',lo_import('"+f.getCanonicalPath()+"\\TEMP\\"+name+"'),...)
which put that file into database. This works pretty good.
Problem occurs when i want to download file from server to client. First i need to put the file back to TEMP dir with SQL command lo_export(...) -> this didn't work (some ERROR when creating a server file, permission denied), so i put the file manually to TEMP dir.
Question is how can i display that file which is stored on server in TEMP dir?
my path to glassfish server temp dir:C:\Program Files (x86)\glassfish-3.1\glassfish\domains\domain1\TEMP\example.pdf
when deploying app url looks like: http://localhost:8080/AppName/
i tried something like that: Window.open("http://localhost:8080/AppName/TEMP/example.pdf", "_blank", "enabled")
My CODE:
Client side:
String link = GWT.getModuleBaseURL() + "filedownloadservlet";
Window.open(link,event.getSelectedItem().getText(),"enabled");
so i pass to servlet located on server side a link and a file name...am i right ?
Server side:
public class FileDownloadServlet extends HttpServlet {
private String path = "TEMP//"; // Your absolute path
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String filename = req.getParameter("filename");
System.out.println(filename); // THIS IS NULL value
File userManualFile = new File(path + filename);
// You can fetch a Blob from the database instead.
ServletOutputStream servletOutputStream = resp.getOutputStream();
resp.setContentType("application/pdf");
resp.addHeader("content-disposition", "attachment; filename=skuska.pdf");
FileInputStream fileInputStream = new FileInputStream(userManualFile);
IOUtils.copy(fileInputStream, servletOutputStream);
servletOutputStream.flush();
When i press a file in Tree widget it shows me a new browser window with this error:
java.io.FileNotFoundException: TEMP\null (The system cannot find the file specified)
You cannot download a file with a RPC call. You must use a normal java servlet. You have to write the bytes into the HttpServletResponse. You can get the bytes from the file in the database by doing an SQL query.
This example is done with spring MVC.
#Controller
public class UserManualServlet {
private String path = "..." // Your absolute path
#RequestMapping("/app/usermanual.download")
public void generateInterceptActivationDeactivationReport(HttpServletRequest request, HttpServletResponse response)
throws IOException
{
String filename = request.getParameter("filename");
File userManualFile = new File(path + filename);
// You can fetch a Blob from the database instead.
ServletOutputStream servletOutputStream = response.getOutputStream();
response.setContentType("application/pdf");
response.addHeader("content-disposition", "attachment; filename=\"user-manual.pdf\"");
FileInputStream fileInputStream = new FileInputStream(userManualFile);
IOUtils.copy(fileInputStream, servletOutputStream);
servletOutputStream.flush();
}
In this example, you can call the URL : ../app/usermanual.download?filename=usermanual.pdf to download the file.

Resources