Uploading images to Spring Boot and S3 all In-Memory - spring

I have an Angular webapp that uses a Spring Boot REST service as its backing web service.
I am adding a "Profiles" feature for users, and as part of this I want to stand up an endpoint that allows users to upload profile images for themselves and immediately upload those files to S3 (where I will host all the images from).
Looking at several Spring Boot/file upload tutorials :
http://www.mkyong.com/spring-boot/spring-boot-file-upload-example/
I update avatar image and display it but the avatar does not change in Spring Boot , why?
Many others
It seems that the standard way of handling such file upload is exposing a controller endpoint that accepts MultipartFiles like so:
#RestController
#RequestMapping("/v1/profiles")
public class ProfileController {
#PostMapping("/photo")
public ResponseEntity uploadProfilePhoto(#RequestParam("mpf") MultipartFile mpf)
// ...
}
Looking at all this code, I can't tell if the MultipartFile instance is in-memory or if Spring sets its location somewhere (perhaps under /tmp?) on the disk.
Looking at the AWS S3 Java SDK tutorial, it seems the standard way to upload a disk-based File is like so:
File file = new File(uploadFileName);
s3client.putObject(new PutObjectRequest(bucketName, keyName, file));
So it looks like I must have a File on disk in order to upload to S3.
I'm wondering if there is a way to keep everything in memory, or whether this is a bad idea and I should stick to disks/File instances!
Is there a way to keep the entire profile image (MultipartFile) in-mempory inside the controller method?
Is there a way to feed (maybe via serialization?!) a MultipartFile instance to S3's PutObjectRequest?
Or is this all a terrible idea (if so, why?!)?

Is there a way to keep the entire profile image (MultipartFile) in-mempory inside the controller method?
No, there is NO way to keep an image File in-memory because File object in java represents a path in file system.
Is there a way to feed (maybe via serialization?!) a MultipartFile instance to S3's PutObjectRequest?
No, from S3's API documentation, there is no way for S3 to deserialize to the image file for you after/during the upload.
Or is this all a terrible idea (if so, why?!)?
It depends on your specific case but it is generally not preferred.
If - there are not many users uploading images at the same time, your memory is probably enough to handle.
Else - You can easily get out-of-memory problems.
If you insist on doing so, S3 API can upload an InputStream (If I remember correctly). You can convert your Multipart File to an InputStream.
This SO thread talks about uploading to S3 with InputStream
You can also take a look at File.createTempFile() to create a temp file.

I have been looking at the same thing. Basically you want a user to be able to be able to upload a photo album and have those photos served from S3 and probably have them secured so only that user can upload/delete/etc.
I believe the simpler answer is in spring boot to get a Pre-signed URL from S3. https://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObjectJavaSDK.html
which basically gives you a token defining the bucket, and object key ("/bobs_profile/smiling_bob.jpg") and a time limit for that image to be uploaded.
Give that to your angular app (or ionic app) to upload the image to that location.
That should do it. but someone let me know if I'm wrong.
The only issue that I see is if bob wants to upload "bobs_nude_photo.jpg" and only wants spring security logged in people to be able to see it... well I'm sure there is an S3 solution for that??

Related

How to return an image file (Byte[]) as a compressed file with Spring API?

I'm the company's file server
Get the file as byte[] through the image path and authentication key.
(This server is not accessible to me.)
What I want to do is, when the user downloads the selected files, I want to compress these files and provide them as a compressed file.
Since the company's file server does not have a download API for multiple files, I think I need to request as many APIs as the number of file lists with a for statement in my service API.
In other words, it seems that we need to take a List<Byte[]> and compress this list.
Is there something wrong with my method?
And can I pass the result as json after compression? (I confirmed that the image file is passed as json.)

Can I serve files stored in Google Cloud Storage via a http.FileServer in golang?

I have developed a small web application that runs a web server in golang.
Each user can login, view the list of their docs (previously uploaded) and click on an item to view an html page that shows some fields of the document plus an tag with a src attribute
The src attribute includes an url like "mydocuments/download/123-456-789.pdf"
On the server side I handle the URL ("mydocuments/download/*") via an http Handler
mymux.HandleFunc(pat.Get("/mydocuments/download/:docname"), DocDownloadHandler)
where:
I check that the user has the rights to view the document in the url
Then I create a fileserver that obviously re-maps the url to the real path of the folder where the files are stored on the filesystem of the server
fileServer := http.StripPrefix("/mydocs/download/",http.FileServer(http.Dir("/the-real-path-to-documents-folder/user-specific-folder/)))
and of course I serve the files
fileServer.ServeHTTP(w, r)
IMPORTANT: the directory where the documents are stored is not the static-files directory I sue for the website but a directory where all files end after being uploaded by users.
My QUESTION
As I am trying to convert the code for it to work also on Google Cloud, I am trying to change the code so that files are stored in a bucket (or, better in "sub-directories" -as they do not properly exist- of a bucket).
How can I modify the code so to map the real document url as available via the cloud storage bucket?
Can I still use the http.FileServer technique above (if so what should I use instead of http.Dir to map the bucket "sub-folder" path where the documents are stored)?
I hope I was enough clear to explain my issue...
I apologise in advance for any unclear point...
Some options are:
Give the user direct access to the resource using a signed URL.
Write code to proxy the request to GCS.
Use http.FS with an fs.FS backed by GCS.
It's possible that a fs.FS for GCS already exists, but you may need to write one.
You can use http.FileSystem since it is an interface and can be implemented however you like.

Spring Boot: Download file directly from directory with file URI

Assume that I have a directory for uploaded public files like images in /var/my-project/upload/public.
I want to download files in public directory with its name. For example if there is a file named product-image.png in public directory with uri /var/my-project/upload/public/product-image.png, access to this file with this url: http://mysite/public/product-image.png.
I know how to use a controller for this purpose, but I want to know is there a way to directly access these files without using a controller method?
The only acceptable way of doing it is with a ReSTful api. There are other methods such as ftp, but When you use ftp, Files can be added by anyone who knows how. With a ReST controller, you define what can be added beforehand. In fact, with a controller you define just about everything regarding that endpoint.
So the short answer is (especially if you are using Spring boot) use an API, lest you not regret it later.
When I was in College, our website had a built in "back door" to a certain endpoint via ftp. Some kids in one of my classes found out about that back door and let themselves inside. Needless to say' they didnt bother wiping their shoes on the mat on their way in.
The moral: Never assume that your resources aren't worth messing with. All you need to do is make it unsecure, and people will eventually mess with it.

Download file and serve it (Spring WebClient -> Liferay Porlet.serveResource)

I want to implement a Liferay Portlet that downloads a ~1GB file from a separate server, and serves it to the website visitor who clicked the link.
The file must be streamed in a memory-efficient way (so no loading everything into memory), and the user should see the download progress shortly after clicking (so no storing everything onto the local disk).
I must use WebClient because it seems to be the standard for making web requests within Liferay 7 (RestTemplate will be deprecated).
I started writing something like this, inspired by an example from the javadoc:
Mono<DataBuffer> bodyMono = client.get()
.uri("https://theotherserver.com/file94875.pdf")
.retrieve()
.bodyToMono(DataBuffer.class);
... which I would feed into the portlet's MVCResourceCommand.serveResource() via PortletResponseUtil.sendFile, which expects a java.io.InputStream.
Unfortunately WebClient gives me a Mono<DataBuffer> (or Flux<DataBuffer>), and another answer claims that reconstructing the InputStream defeats the purpose of using WebClient in the first place.
What would be the most efficient and WebClient-savvy way to implement this?
In case of Liferay, the documentation states, that you can use ....getPortletOutputStream() to retrieve an OutputStream.
After setting contentlengh (so browser knows how much to expect), you can use this: Convert writes to OutputStream into a Flux<DataBuffer> usable by ServerResponse
To write your data to the OutputStream

How to use ExternalDirectory Resource

How to use External Directory to Store Images.
And how i access that images thru my Web application ?
I am using Jboss as an application Server.
Web application is in Java,Jsp.
Presently images stored in WAR file.
After google i got the solution
C:\jboss-4.0.0\server\default\deploy\jbossweb-tomcat55.sar\server.xml
Then restart the server and access the
http://localhost:8080/contextname/images
Please provide comments
I've answered similar question before: Simplest way to serve static data from outside the application server in a Java web application
To summarize there are basically two ways:
Add a new Context to the server.xml denoting the absolute location where the images are.
Create a Servlet which gets an InputStream of the image using FileInputStream and writes it the usual Java IO way to the OutputStream of the response, along with at least Content-Type, Content-Length and Content-Disposition headers.
See the link for more detailed answers and code examples.

Resources