0
votes

I'm creating a web app using Java servlets and JSPs and I want to create an upload form in JSP for my clients to be able to upload and download stuff. I'm using Cloud Storage and my default bucket to upload stuff. I followed Google's tutorial on Reading and Writing to Google Cloud Storage.

This is my Servlet:

public class Create extends HttpServlet {

    public static final boolean SERVE_USING_BLOBSTORE_API = false;

    private final GcsService gcsService = GcsServiceFactory.createGcsService(new RetryParams.Builder()
            .initialRetryDelayMillis(10)
            .retryMaxAttempts(10)
            .totalRetryPeriodMillis(15000)
            .build());

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        GcsFilename fileName = getFileName(req);
        if (SERVE_USING_BLOBSTORE_API) {
            BlobstoreService blobstoreService =  BlobstoreServiceFactory.getBlobstoreService();
            BlobKey blobKey = blobstoreService.createGsBlobKey(
                    "/gs/" + fileName.getBucketName() + "/" + fileName.getObjectName());
            blobstoreService.serve(blobKey, resp);
        } else {
            GcsInputChannel readChannel = gcsService.openPrefetchingReadChannel(fileName, 0, BUFFER_SIZE);
            copy(Channels.newInputStream(readChannel), resp.getOutputStream());
        }
    }

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        GcsFileOptions instance = GcsFileOptions.getDefaultInstance();
        GcsFilename fileName = getFileName(req);
        GcsOutputChannel outputChannel;
        outputChannel = gcsService.createOrReplace(fileName, instance);
        copy(req.getInputStream(), Channels.newOutputStream(outputChannel));
    }

    private GcsFilename getFileName(HttpServletRequest req) {
        String[] splits = req.getRequestURI().split("/", 4);
        if (!splits[0].equals("") || !splits[1].equals("gcs")) {
            throw new IllegalArgumentException("The URL is not formed as expected. " +
                    "Expecting /gcs/<bucket>/<object>");
        }
        return new GcsFilename(splits[2], splits[3]);
    }

    private void copy(InputStream input, OutputStream output) throws IOException {
        try {
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead = input.read(buffer);
            while (bytesRead != -1) {
                output.write(buffer, 0, bytesRead);
                bytesRead = input.read(buffer);
            }
        } finally {
            input.close();
            output.close();
        }
    }
}

I can upload and download successfully, but only text and not real files like images, pdfs, etc., which is my problem. This tutorial is for reading and writing text but i want to upload real files. As you can see from my jsp the enctype is "text/plain":

<form action="/index.html" enctype="text/plain" method="get" name="putFile" id="putFile">
      <div>
        Bucket: <input type="text" name="bucket" />
        File Name: <input type="text" name="fileName" />
        <br /> File Contents: <br />
        <textarea name="content" id="content" rows="3" cols="60"></textarea>
        <br />
        <input type="submit" onclick='uploadFile(this)' value="Upload Content" />
      </div>
    </form>

I tried to change it to "multipart/form-data" and put a

<input name="content" id="content" type="file">

but this does not upload the real file only the fake path of the file. And I want to know how to upload real files, any help would be appreciated.

2

2 Answers

0
votes

Here is one example on how to upload blobs to Cloud Storage:

First you initialize the storage with these lines:

private static Storage storage = null;

  // [START init]
  static {
    storage = StorageOptions.getDefaultInstance().getService();
  }
  // [END init]

You may change the code to accept different file extensions according to your needs on the getImageUrl method in the line String[] allowedExt = {"jpg", "jpeg", "png", "gif"};

/**
 * Extracts the file payload from an HttpServletRequest, checks that the file extension
 * is supported and uploads the file to Google Cloud Storage.
 */
public String getImageUrl(HttpServletRequest req, HttpServletResponse resp,
                          final String bucket) throws IOException, ServletException {
  Part filePart = req.getPart("file");
  final String fileName = filePart.getSubmittedFileName();
  String imageUrl = req.getParameter("imageUrl");
  // Check extension of file
  if (fileName != null && !fileName.isEmpty() && fileName.contains(".")) {
    final String extension = fileName.substring(fileName.lastIndexOf('.') + 1);
    String[] allowedExt = {"jpg", "jpeg", "png", "gif"};
    for (String s : allowedExt) {
      if (extension.equals(s)) {
        return this.uploadFile(filePart, bucket);
      }
    }
    throw new ServletException("file must be an image");
  }
  return imageUrl;
}

Here a timestamp is appended in the filename which can be a good idea if you want to make the filename unique.

/**
 * Uploads a file to Google Cloud Storage to the bucket specified in the BUCKET_NAME
 * environment variable, appending a timestamp to end of the uploaded filename.
 */
@SuppressWarnings("deprecation")
public String uploadFile(Part filePart, final String bucketName) throws IOException {
  DateTimeFormatter dtf = DateTimeFormat.forPattern("-YYYY-MM-dd-HHmmssSSS");
  DateTime dt = DateTime.now(DateTimeZone.UTC);
  String dtString = dt.toString(dtf);
  final String fileName = filePart.getSubmittedFileName() + dtString;

  // the inputstream is closed by default, so we don't need to close it here
  BlobInfo blobInfo =
      storage.create(
          BlobInfo
              .newBuilder(bucketName, fileName)
              // Modify access list to allow all users with link to read file
              .setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(), Role.READER))))
              .build(),
          filePart.getInputStream());
  // return the public download link
  return blobInfo.getMediaLink();
}

In this documentation you'll find more details: https://cloud.google.com/java/getting-started/using-cloud-storage#uploading_blobs_to_cloud_storage

The complete code for this example is in github: https://github.com/GoogleCloudPlatform/getting-started-java/blob/master/bookshelf/3-binary-data/src/main/java/com/example/getstarted/util/CloudStorageHelper.java

0
votes

I found a solution.

This is my JSP:

<form action="/create" enctype="multipart/form-data" method="post" name="putFile" id="putFile">
      <div>
        File Name: <input type="text" name="fileName" />
        <br /> File Contents: <br />
        <input type="submit" value="Upload Content" />
      </div>
</form>

When i submit the form, it goes into this Servlet:

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    Part filePart = req.getPart("content"); /*Get file from jsp*/

    /*Get file name of file from jsp*/
    String name = Paths.get(filePart.getSubmittedFileName()).getFileName().toString();
    GcsFileOptions instance = GcsFileOptions.getDefaultInstance();
    GcsFilename fileName = new GcsFilename(BUCKET_NAME, name);
    GcsOutputChannel outputChannel;
    outputChannel = gcsService.createOrReplace(fileName, instance);

    /*Pass the file to copy function, wich uploads the file to cloud*/
    copy(filePart.getInputStream(), Channels.newOutputStream(outputChannel));
    req.getRequestDispatcher("download.jsp").forward(req, resp);
}

private GcsFilename getFileName(HttpServletRequest req) {
    String[] splits = req.getRequestURI().split("/", 4);
    if (!splits[0].equals("") || !splits[1].equals("gcs")) {
        throw new IllegalArgumentException("The URL is not formed as expected. " +
            "Expecting /gcs/<bucket>/<object>");
    }
    return new GcsFilename(splits[2], splits[3]);
}

private void copy(InputStream input, OutputStream output) throws IOException {
    try {
        byte[] buffer = new byte[BUFFER_SIZE];
        int bytesRead = input.read(buffer);
        while (bytesRead != -1) {
            output.write(buffer, 0, bytesRead);
            bytesRead = input.read(buffer);
        }
    } finally {
        input.close();
        output.close();
    }
}