Spring Boot store upload file - java

I have to store an image for a few minutes after user upload it to show a preview, before confirmation, after confirmation i need get it back and persist.
I would like to know the best pratice to do this.
I saw about Cache and Caffeine, but i don't know if this is the best pratice and how store in Cache with a random hash to get it back after
[EDIT]
Maybe I was overestimating the problem
Following the #Robert suggestion i'll use temporary files, but i still need some way to garantee that files will be deleted. So i created a new question and i'll keep this to help others that do search with these terms.
Follow the link
How guarantee the file will be deleted after automatically some time?

I do this in one of my apps.
In the upload POST, I save the image to a temporary file and then store the temporary file name in a session attribute. I use the session attribute because the image being previewed shouldn't be visible to any other users until it has been written to persistent storage.
In the subsequent GET, I pull the temporary file name out of the session and stream it out to the response, deleting it when finished. I don't bother keeping the file after the preview is rendered as I don't need it anymore.
See the full implementation below:
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
#RestController
#RequestMapping("/api/imagePreview")
public class ImagePreviewController
{
#PostMapping
public ResponseEntity<?> post(HttpSession session, #RequestPart MultipartFile file) throws IOException
{
if (file.getContentType() != null && file.getContentType().startsWith("image/")) {
Path tempFile = Files.createTempFile("", file.getOriginalFilename());
file.transferTo(tempFile.toFile());
session.setAttribute("previewImage", tempFile.toFile().getPath());
session.setAttribute("previewImageContentType", file.getContentType());
return ResponseEntity.status(HttpStatus.CREATED).build();
} else {
return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).build();
}
}
#GetMapping
public void get(HttpServletRequest request, HttpServletResponse response) throws IOException
{
HttpSession session = request.getSession(false);
if (session == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
String path = (String) session.getAttribute("previewImage");
String contentType = (String) session.getAttribute("previewImageContentType");
if (path == null || contentType == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
response.setContentType(contentType);
try (OutputStream out = response.getOutputStream()) {
Files.copy(Paths.get(path), out);
} finally {
Files.deleteIfExists(Paths.get(path));
}
}
}

Related

Java Spring - Can't save image to static folder

I want to save image to resources/static/photos file, but Java/Kotlin can't find it. It finds project/photos well though.
This is a code, in Kotlin, but I don't think it matters
override fun saveImage(imageFile: MultipartFile, id: String) {
val bytes = imageFile.bytes
val path = Paths.get(
"$imagesFolderPath$id.${imageFile.originalFilename.substringAfter('.')}")
Files.write(path, bytes)
}
I need this to be saved to resources/static/photos to be able to access it from thymeleaf.
Thanks.
The problem is, you may be able to save files inside your projects directory during the development phase, but that won't be possible as soon as you export your project as an application package (a .jar-application, .war-archive etc), because at that point, everything that previously was an actual directory on your file-system is now a single file.
Here's an example how you could implement this by saving the images in a configurable folder:
I never wrote a line of code in Kotlin. I hope this example helps you even if it is in Java.
This is an example controller that accepts images to be uploaded on a POST endpoint and being downloaded on a GET endpoint:
package example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Optional;
#RestController
public class MyController {
private final Path imageStorageDir;
/*
The target path can be configured in the application.properties / application.yml or using the parameter -Dimage-storage.dir=/some/path/
*/
#Autowired
public MyController(#Value("${image-storage-dir}") Path imageStorageDir) {
this.imageStorageDir = imageStorageDir;
}
#PostConstruct
public void ensureDirectoryExists() throws IOException {
if (!Files.exists(this.imageStorageDir)) {
Files.createDirectories(this.imageStorageDir);
}
}
/*
This enables you to perform POST requests against the "/image/YourID" path
It returns the name this image can be referenced on later
*/
#PostMapping(value = "/image/{id}", produces = MediaType.TEXT_PLAIN_VALUE)
public String uploadImage(#RequestBody MultipartFile imageFile, #PathVariable("id") String id) throws IOException {
final String fileExtension = Optional.ofNullable(imageFile.getOriginalFilename())
.flatMap(MyController::getFileExtension)
.orElse("");
final String targetFileName = id + "." + fileExtension;
final Path targetPath = this.imageStorageDir.resolve(targetFileName);
try (InputStream in = imageFile.getInputStream()) {
try (OutputStream out = Files.newOutputStream(targetPath, StandardOpenOption.CREATE)) {
in.transferTo(out);
}
}
return targetFileName;
}
/*
This enables you to download previously uploaded images
*/
#GetMapping("/image/{fileName}")
public ResponseEntity<Resource> downloadImage(#PathVariable("fileName") String fileName) {
final Path targetPath = this.imageStorageDir.resolve(fileName);
if (!Files.exists(targetPath)) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(new PathResource(targetPath));
}
private static Optional<String> getFileExtension(String fileName) {
final int indexOfLastDot = fileName.lastIndexOf('.');
if (indexOfLastDot == -1) {
return Optional.empty();
} else {
return Optional.of(fileName.substring(indexOfLastDot + 1));
}
}
}
Let's say you uploaded am image with the file-ending .png and the id HelloWorld, you could then access the image using the url:
http://localhost:8080/image/HelloWorld.png
Using this URL you can also reference the image in any of your thymeleaf templates:
<img th:src="#{/image/HelloWorld.png}"></img>

Fetch thymeleaf fragments from external URL

I have a spring-boot/thymeleaf website on server A, and I want to load some fragments from server B. The fragments are dynamic and some of them call java methods defined in server A, so what I need is to fetch those fragments (as plain text?) from server B and include them in my html pages in server A, where they will be processed etc. Server B will act like a repository, it won't do any processing at all, just serve the fragments to server A.
Is this possible?
Ok, I posted this question because all my attempts were failing, but after all it was just a typo that was holding me back... So here's what worked for me, in case anyone is interested:
I saved the fragments in src/main/resources/static/fragments on server B. Let's assume a file named frg with a fragment called "content" inside it.
I created a controller in server B to serve the files as plain text, like this:
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.File;
import java.nio.file.Files;
import javax.servlet.http.HttpServletResponse;
#Controller
public class FragmentsController {
#RequestMapping(value = "/fragments/{fragmentPage}")
#ResponseBody
public String GetFragment (#PathVariable String fragmentPage, HttpServletResponse response) throws Exception {
response.setHeader("Content-Type", "text/plain");
response.setHeader("success", "no");
if (fragmentPage == null)
{
System.err.println("Nothing to serve!");
return null;
}
System.out.println("Serving fragment: " + fragmentPage);
String fileName = "static/fragments/"+fragmentPage;
File resource = new ClassPathResource(fileName).getFile();
String frg = "";
try
{
frg= new String(Files.readAllBytes(resource.toPath()));
response.setHeader("success", "yes");
}
catch (Exception e)
{
frg = "Error loading fragment: " + e.getMessage();
}
return frg;
}
}
From server A, I can now fetch the fragment like this:
<div th:include="http://<server_b_url:port>/fragments/frg :: content"></div>

JavaEE and Firebase admin sdk - setValueAsync not pushing data to realtime firebase

Am using firebase admin sdk and JavaEE on intellij built on gradle and glassfish server.
am trying to push a value to realtime database, but sadly am unable to do so. I've been searching online for weeks now and gotten nothing. I also followed some solutions in stackoverflow answers like : Firebase Java Admin SDK don't work but nothing works for me.
I've read a lot of reasons why such a problem would occur with firebase admin sdk but i have no solutions.
here's my code:
package sample;
package sample;
import com.google.api.core.ApiFuture;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseToken;
import com.google.firebase.auth.UserRecord;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import sample.model.FireBaseAuth;
import sample.model.FireBaseUtils;
import sample.model.Owner;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
#WebServlet("/success")
public class SuccessServlet extends HttpServlet {
public void init() throws ServletException {
super.init();
FireBaseUtils.initilizeFirebase();
}
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String email = request.getParameter("email");
String password = request.getParameter("pass");
//System.out.println(name);
try{
//a hashmap for the number of shopsOwned
HashMap<String, String> shopsOwned = new HashMap<>();
shopsOwned.put("shopName" , "shopName");
//get the database instance and the database reference
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("Business");
DatabaseReference ownersRef = ref.child("Owners"); //further get the reference to the owners node
//create a new owner with the values of the new user, using the Owner class
Owner newOwner = new Owner("userRecord2.getUid()", "userRecord2.getDisplayName()",
"userRecord2.getPhoneNumber()", "userRecord2.getEmail()", shopsOwned);
//create a hashmap of the users, in this case, just one user
Map<String, Owner> users = new HashMap<>();
users.put("userRecord2getPhoneNumber", newOwner); //add the new owner to the hashmap
System.out.println("this is the user :" + newOwner.getFull_name());
//push the new owner hashmap to the database reference
ApiFuture<Void> future = ownersRef.push().setValueAsync(users);
//Object o = future.get(8, TimeUnit.SECONDS);
System.out.println(future.isDone());
//System.out.println(future.isDone());
request.getRequestDispatcher("success.jsp").forward(request, response);
}catch(Exception e){e.printStackTrace();}
}
#Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
doGet(request, response);
}
}
any ideas will be appreciated.
Edit : I dont get any errors whatsoever, the webapp runs normally but the realtime db at firebase isn't updated
You need to wait until the future is complete, before the request thread is returned. Otherwise there's no guarantee that the update is completed, and any errors are silently discarded. So try something like the following:
ApiFuture<Void> future = ownersRef.push().setValueAsync(users);
future.get();
request.getRequestDispatcher("success.jsp").forward(request, response);
Writing to Firestore (like interaction with most cloud APIs) happens asynchronously, and on a different thread. When you call future.isDone(), the operation isn't done yet.
You'll want to add a callback that gets called when the operation has completed:
ApiFuture<Void> future = ownersRef.push().setValueAsync(users);
future.addCallback(future, new ApiFutureCallback<String>() {
#Override
public void onSuccess(String result) {
System.out.println("Operation completed with result: " + result);
System.out.println(future.isDone());
request.getRequestDispatcher("success.jsp").forward(request, response);
}
#Override
public void onFailure(Throwable t) {
System.err.println("Operation failed with error: " + t);
}
Also see:
Firebase: Asynchronous Operations with Admin Java SDK

How to set response header for digital asset in AEM CQ6.1?

I have a request that if a user directly accesses a .pdf asset(for example, http://localhost:4505/content/dam/company/us/en/962059.pdf) from AEM CQ 6.1, I need to send a custom http respone header. This is what I wrote. This is only works if I open a .html page. But it doesn't work if I open a .pdf in browser. So what did I do wrong?
Thanks
package com.mycompany.wcm.filter;
import org.apache.felix.scr.annotations.*;
import org.apache.felix.scr.annotations.sling.SlingFilter;
import org.apache.felix.scr.annotations.sling.SlingFilterScope;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.*;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.api.scripting.SlingScriptHelper;
import org.apache.sling.runmode.RunMode;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Session;
import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
#SlingFilter(
label = "Sample Filter",
description = "Sample Description",
metatype = true,
generateComponent = true, // True if you want to leverage activate/deactivate
generateService = true,
order = -501, // The smaller the number, the earlier in the Filter chain (can go negative);
scope = SlingFilterScope.REQUEST)
#Properties({
#Property(
label = "Vendor",
name = "service.vendor",
value = "SampleVendor",
propertyPrivate = true
)
})
public class AssetFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
final SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) response;
slingResponse.setHeader("myheader1","no-cache");
slingResponse.setHeader("myheader2","no-store");
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
Just a update, initialy I thought the problem only happens to .pdf file, but this also happens to image file. So I created a regular JSP web application, and this code is working for regular web app. Is there something funny on AEM side?

Map a image file through Spring controller

Is there any way to map a image file using a spring controller? In my spring application, I want store the images in the directory src/main/resources (i'm using maven) and access them with a method like this:
#RequestMapping(value="image/{theString}")
public ModelAndView image(#PathVariable String theString) {
return new ModelAndView('what should be placed here?');
}
the string theString it's the image name (without extension). With this approach, I should be able to access my images this way:
/webapp/controller_mapping/image/image_name
Anyone can point a direction to do that?
You can return HttpEntity<byte[]>. Construct new instance providing image byte array and necessary headers like content length and mime type then return it from your method. Image bytes can be obtained using classloader getResourceAsStream method.
This works for me. It could use some cleaning up but it works. The ServiceException is just a simple base exception.
Good Luck!
package com.dhargis.example;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
#RequestMapping("/image")
public class ImageController {
private static final Logger log = Logger.getLogger(ImageController.class);
private String filestore = "C:\\Users\\dhargis";
//produces = "application/octet-stream"
#RequestMapping(value = "/{filename:.+}", method = RequestMethod.GET)
public void get( #PathVariable String filename,
HttpServletRequest request,
HttpServletResponse response) {
log.info("Getting file " + filename);
try {
byte[] content = null;
File store = new File(filestore);
if( store.exists() ){
File file = new File(store.getPath()+File.separator+filename);
if( file.exists() ){
content = FileUtils.readFileToByteArray(file);
} else {
throw new ServiceException("File does not exist");
}
} else {
throw new ServiceException("Report store is required");
}
ServletOutputStream out = response.getOutputStream();
out.write(content);
out.flush();
out.close();
} catch (ServiceException e) {
log.error("Error on get", e);
} catch (IOException e) {
log.error("Error on get", e);
}
}
}
<!-- begin snippet: js hide: false -->

Categories