Spring MVC how to access Static Resource from Controller - java

Okay I'm working with Spring MVC 4.0 and I'm having a problem reading a txt file from a Controller.
I set in my dispatcher-servlet
<mvc:resources mapping="/docs/**" location="/docs/"/>
so at docs I set a file.txt, I want to read that file from a controller.
#RequestMapping("/file")
public class FileController {
#RequestMapping(method=RequestMethod.GET)
public String getFile() throws IOException{
BufferedReader br = new BufferedReader(new FileReader("docs/file.txt"));
StringBuilder sb = new StringBuilder();
try {
String line = br.readLine();
while (line != null) {
sb.append(line);
line = br.readLine();
}
} finally {
br.close();
}
return sb.toString();
}
}
I have tried all the paths for FileReader(path) and I can't get that file... how can I do it?
My directory structure is:
Application
---WepPages
-------META-INF
-------WEB-INF
-------docs
---SourcePackages
---Libraries
.
.
.
.
.

I needed to do this to aggregate a list of js and css files for my view.
The file paths can be passed to the view so they don't need to be registered manually.
This is how I am doing it -
#Controller
public class HomeController {
WebApplicationContext webappContext;
List<String> jsFiles = new ArrayList<>();
List<String> cssFiles = new ArrayList<>();
#Autowired
public HomeController(ServletContext servletContext) throws IOException{
webappContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
Resource[] jsResources = webappContext.getResources("content/modules/**/*.js");
Resource[] cssResources = webappContext.getResources("content/modules/**/*.css");
for (Resource resource : jsResources) {
jsFiles.add(resource.getURL().getPath());
}
for (Resource resource : cssResources) {
cssFiles.add(resource.getURL().getPath());
}
}
#RequestMapping({ "/", "/home**" })
public ModelAndView getHomePage() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("home");
modelAndView.addObject("jsFiles", jsFiles);
modelAndView.addObject("cssFiles", cssFiles);
return modelAndView;
}
}

The resources are typically packed to war. That's why you can't find them in file system. Though you can still access them using classloader:
getClass().getResourceAsStream("/docs/file.txt")

Spring can access underlying resources by using Resource interface:
#Value("file:/docs/file.txt")
private Resource myFile;
#RequestMapping(method = RequestMethod.GET)
public String getFile() throws IOException {
BufferedReader br = new BufferedReader(new FileReader(myFile.getFile()));
// do something
}

You can read the file using the ServletContext. e.g.
ServletContext context = //...
InputStream is = context.getResourceAsStream("/docs/file.txt");
Also, check this - ServletContext and Spring MVC.

Related

SpringBoot , how can i set MultipartFile size limit base on each controller or requestmapping

how can I set MultipartFile size limit base on each controller or request mapping
I think this set is a global setting.
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
if I have two endpoints.
1.POST /images <-
This path should be limited to 1M
2.POST /videos <-
This path should be limited to 50M
how can I limit each endpoint?
You have to declare a Bean of type MultipartResolver
#Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver
= new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(10485760)
return multipartResolver;
Below controller will help you to limit the size. If you exceed the upload size that will provide an MaxUploadSizeExceededException
#RequestMapping(value = "/images", method = RequestMethod.POST)
public ModelAndView uploadFile(MultipartFile file) throws IOException {
ModelAndView modelAndView = new ModelAndView("file");
InputStream in = file.getInputStream();
File currDir = new File(".");
String path = currDir.getAbsolutePath();
FileOutputStream f = new FileOutputStream(
path.substring(0, path.length()-1)+ file.getOriginalFilename());
int ch = 0;
while ((ch = in.read()) != -1) {
f.write(ch);
}
f.flush();
f.close();
modelAndView.getModel().put("message", "File has uploaded" );
return modelAndView;
}

How to read Long blob from mysql in java? [duplicate]

I'm getting image data (as byte[]) from DB. How to return this image in #ResponseBody ?
EDIT
I did it without #ResponseBody using HttpServletResponse as method parameter:
#RequestMapping("/photo1")
public void photo(HttpServletResponse response) throws IOException {
response.setContentType("image/jpeg");
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
IOUtils.copy(in, response.getOutputStream());
}
Using #ResponseBody with registered org.springframework.http.converter.ByteArrayHttpMessageConverter converter as #Sid said doesn't work for me :(.
#ResponseBody
#RequestMapping("/photo2")
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
if you are using Spring version of 3.1 or newer you can specify "produces" in #RequestMapping annotation. Example below works for me out of box. No need of register converter or anything else if you have web mvc enabled (#EnableWebMvc).
#ResponseBody
#RequestMapping(value = "/photo2", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
With Spring 4.1 and above, you can return pretty much anything (such as pictures, pdfs, documents, jars, zips, etc) quite simply without any extra dependencies. For example, the following could be a method to return a user's profile picture from MongoDB GridFS:
#RequestMapping(value = "user/avatar/{userId}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<InputStreamResource> downloadUserAvatarImage(#PathVariable Long userId) {
GridFSDBFile gridFsFile = fileService.findUserAccountAvatarById(userId);
return ResponseEntity.ok()
.contentLength(gridFsFile.getLength())
.contentType(MediaType.parseMediaType(gridFsFile.getContentType()))
.body(new InputStreamResource(gridFsFile.getInputStream()));
}
The things to note:
ResponseEntity with InputStreamResource as a return type
ResponseEntity builder style creation
With this method you dont have to worry about autowiring in the HttpServletResponse, throwing an IOException or copying stream data around.
In addition to registering a ByteArrayHttpMessageConverter, you may want to use a ResponseEntity instead of #ResponseBody. The following code works for me :
#RequestMapping("/photo2")
public ResponseEntity<byte[]> testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.CREATED);
}
By using Spring 3.1.x and 3.2.x, this is how you should do it:
The controller method:
#RequestMapping("/photo2")
public #ResponseBody byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
And the mvc annotation in servlet-context.xml file:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>image/jpeg</value>
<value>image/png</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
In addition to a couple of answers here a few pointers (Spring 4.1).
Incase you don't have any messageconverters configured in your WebMvcConfig, having ResponseEntity inside your #ResponseBody works well.
If you do, i.e. you have a MappingJackson2HttpMessageConverter configured (like me) using the ResponseEntity returns a org.springframework.http.converter.HttpMessageNotWritableException.
The only working solution in this case is to wrap a byte[] in the #ResponseBody as follows:
#RequestMapping(value = "/get/image/{id}", method=RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
public #ResponseBody byte[] showImageOnId(#PathVariable("id") String id) {
byte[] b = whatEverMethodUsedToObtainBytes(id);
return b;
}
In this case do rememeber to configure the messageconverters properly (and add a ByteArrayHttpMessageConverer) in your WebMvcConfig, like so:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
converters.add(byteArrayHttpMessageConverter());
}
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper);
return converter;
}
#Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
return arrayHttpMessageConverter;
}
private List<MediaType> getSupportedMediaTypes() {
List<MediaType> list = new ArrayList<MediaType>();
list.add(MediaType.IMAGE_JPEG);
list.add(MediaType.IMAGE_PNG);
list.add(MediaType.APPLICATION_OCTET_STREAM);
return list;
}
I prefere this one:
private ResourceLoader resourceLoader = new DefaultResourceLoader();
#ResponseBody
#RequestMapping(value = "/{id}", produces = "image/bmp")
public Resource texture(#PathVariable("id") String id) {
return resourceLoader.getResource("classpath:images/" + id + ".bmp");
}
Change the media type to what ever image format you have.
In your application context declare a AnnotationMethodHandlerAdapter and registerByteArrayHttpMessageConverter:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list>
<bean id="byteArrayMessageConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
</util:list>
</property>
</bean>
also in the handler method set appropriate content type for your response.
#RequestMapping(value = "/get-image",method = RequestMethod.GET)
public ResponseEntity<byte[]> getImage() throws IOException {
RandomAccessFile f = new RandomAccessFile("/home/vivex/apache-tomcat-7.0.59/tmpFiles/1.jpg", "r");
byte[] b = new byte[(int)f.length()];
f.readFully(b);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(b, headers, HttpStatus.CREATED);
}
Worked For Me.
You should specify the media type in the response. I'm using a #GetMapping annotation with produces = MediaType.IMAGE_JPEG_VALUE. #RequestMapping will work the same.
#GetMapping(value="/current/chart",produces = MediaType.IMAGE_JPEG_VALUE)
#ResponseBody
public byte[] getChart() {
return ...;
}
Without a media type, it is hard to guess what is actually returned (includes anybody who reads the code, browser and of course Spring itself). A byte[] is just not specific. The only way to determine the media type from a byte[] is sniffing and guessing around.
Providing a media type is just best practice
It's work for me in Spring 4.
#RequestMapping(value = "/image/{id}", method = RequestMethod.GET)
public void findImage(#PathVariable("id") String id, HttpServletResponse resp){
final Foto anafoto = <find object>
resp.reset();
resp.setContentType(MediaType.IMAGE_JPEG_VALUE);
resp.setContentLength(anafoto.getImage().length);
final BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(anafoto.getImageInBytes()));
try {
FileCopyUtils.copy(in, resp.getOutputStream());
resp.flushBuffer();
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Non of the answers worked for me, so I've managed to do it like that:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("your content type here"));
headers.set("Content-Disposition", "attachment; filename=fileName.jpg");
headers.setContentLength(fileContent.length);
return new ResponseEntity<>(fileContent, headers, HttpStatus.OK);
Setting Content-Disposition header I was able to download the file with the #ResponseBody annotation on my method.
This is how I do it with Spring Boot and Guava:
#RequestMapping(value = "/getimage", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public void getImage( HttpServletResponse response ) throws IOException
{
ByteStreams.copy( getClass().getResourceAsStream( "/preview-image.jpg" ), response.getOutputStream() );
}
In spring 4 it's very easy you don't need to make any changes in beans. Only mark your return type to #ResponseBody.
Example:-
#RequestMapping(value = "/image/{id}")
public #ResponseBody
byte[] showImage(#PathVariable Integer id) {
byte[] b;
/* Do your logic and return
*/
return b;
}
I think you maybe need a service to store file upload and get that file.
Check more detail from here
1) Create a Storage Sevice
#Service
public class StorageService {
Logger log = LoggerFactory.getLogger(this.getClass().getName());
private final Path rootLocation = Paths.get("upload-dir");
public void store(MultipartFile file) {
try {
Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
} catch (Exception e) {
throw new RuntimeException("FAIL!");
}
}
public Resource loadFile(String filename) {
try {
Path file = rootLocation.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("FAIL!");
}
} catch (MalformedURLException e) {
throw new RuntimeException("FAIL!");
}
}
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
public void init() {
try {
Files.createDirectory(rootLocation);
} catch (IOException e) {
throw new RuntimeException("Could not initialize storage!");
}
}
}
2) Create Rest Controller to upload and get file
#Controller
public class UploadController {
#Autowired
StorageService storageService;
List<String> files = new ArrayList<String>();
#PostMapping("/post")
public ResponseEntity<String> handleFileUpload(#RequestParam("file") MultipartFile file) {
String message = "";
try {
storageService.store(file);
files.add(file.getOriginalFilename());
message = "You successfully uploaded " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.OK).body(message);
} catch (Exception e) {
message = "FAIL to upload " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(message);
}
}
#GetMapping("/getallfiles")
public ResponseEntity<List<String>> getListFiles(Model model) {
List<String> fileNames = files
.stream().map(fileName -> MvcUriComponentsBuilder
.fromMethodName(UploadController.class, "getFile", fileName).build().toString())
.collect(Collectors.toList());
return ResponseEntity.ok().body(fileNames);
}
#GetMapping("/files/{filename:.+}")
#ResponseBody
public ResponseEntity<Resource> getFile(#PathVariable String filename) {
Resource file = storageService.loadFile(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
.body(file);
}
}
When using produces with MediaType.IMAGE_JPEG_VALUE, make sure that you are returning byte[], but not Byte[]. Very strange, but spring cannot convert it and raises an exception: no converter found.

Get all path variable

I want to develop a service that return a json file.
#RequestMapping(value = "/{fileName}/**", method = RequestMethod.GET, produces = { "application/json" })
public String jsonREST(#PathVariable String fileName) {
StringBuilder jsonBuilder = new StringBuilder();
logger.info("===> File name: " + fileName);
try {
BufferedReader bf = new BufferedReader(new FileReader("fileName + ".json"));
String line = null;
while ((line = bf.readLine()) != null) {
jsonBuilder.append(line);
}
} catch (Exception e) {
System.out.println("Error Parsing: - ");
}
return jsonBuilder.toString();
}
I need to get the path for example if the json file is in subdirectory or else.
use cases:
localhost:8080/my-directory/my-sub-dir/my-json-file
localhost:8080/my-json-file
Would you have any idea how I can get the hole path for example
my-directory/my-sub-dir/my-json-file
Or if you have another solution to do the same job, I will be very happy for that
Best regards
You can get the full request url by having Spring inject the HttpServletRequest and getting it as follows:
#RequestMapping(value = "/{fileName}/**", method = RequestMethod.GET, produces = { "application/json" })
public String jsonREST(HttpServletRequest request, #PathVariable String fileName) {
String uri = request.getRequestURI();
//Do your stuff here
}
Seems like you don't need a servlet container to achieve this. If I get what you are trying to do, you want to serve the json files statically. Try tweaking this:
https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot

How to offer a file compressed as download via spring #RestController?

I have a servlet that offers a CSV file for download:
#RestController
#RequestMapping("/")
public class FileController {
#RequestMapping(value = "/export", method = RequestMethod.GET)
public FileSystemResource getFile() {
return new FileSystemResource("c:\file.csv");
}
}
This works just fine.
Question: how can I offer this file as compressed file? (zip, gzip, tar doesn't matter)?
Based on the solution here (for a plain Servlet), you can also do the same with a Spring MVC based controller.
#RequestMapping(value = "/export", method = RequestMethod.GET)
public void getFile(OutputStream out) {
FileSystemResource resource = new FileSystemResource("c:\file.csv");
try (ZipOutputStream zippedOut = new ZipOutputStream(out)) {
ZipEntry e = new ZipEntry(resource.getName());
// Configure the zip entry, the properties of the file
e.setSize(resource.contentLength());
e.setTime(System.currentTimeMillis());
// etc.
zippedOut.putNextEntry(e);
// And the content of the resource:
StreamUtils.copy(resource.getInputStream(), zippedOut);
zippedOut.closeEntry();
zippedOut.finish();
} catch (Exception e) {
// Do something with Exception
}
}
You created a ZipOutputStream based on the responses OutputStream (which you can simply have injected into the method). Then create an entry for the zipped out stream and write it.
Instead of the OutputStream you could also wire the HttpServletResponse so that you would be able to set the name of the file and the content type.
#RequestMapping(value = "/export", method = RequestMethod.GET)
public void getFile(HttpServletResponse response) {
FileSystemResource resource = new FileSystemResource("c:\file.csv");
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=file.zip");
try (ZipOutputStream zippedOut = new ZipOutputStream(response.getOutputStream())) {
ZipEntry e = new ZipEntry(resource.getName());
// Configure the zip entry, the properties of the file
e.setSize(resource.contentLength());
e.setTime(System.currentTimeMillis());
// etc.
zippedOut.putNextEntry(e);
// And the content of the resource:
StreamUtils.copy(resource.getInputStream(), zippedOut);
zippedOut.closeEntry();
zippedOut.finish();
} catch (Exception e) {
// Do something with Exception
}
}
Untested but something like that should work:
final Path zipTmpPath = Paths.get("C:/file.csv.zip");
final ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipTmpPath, StandardOpenOption.WRITE));
final ZipEntry zipEntry = new ZipEntry("file.csv");
zipOut.putNextEntry(zipEntry);
Path csvPath = Paths.get("C:/file.csv");
List<String> lines = Files.readAllLines(csvPath);
for(String line : lines)
{
for(char c : line.toCharArray())
{
zipOut.write(c);
}
}
zipOut.flush();
zipOut.close();
return new FileSystemResource("C:/file.csv.zip");
use this :
#RequestMapping(value = "/zip", produces="application/zip")
This may solve your issue

Load a static file in a Spring Controller

I'm pretty new to Spring, so apologies if I don't see the obvious answer here.
I set up small demo project with a Spring MVC controller and deployed it to App Engine. In my controller I would like to read the content of a static file into a String. What's the best way of doing that?
I googled a bit but I'm probably searching for the wrong thing. I tried the below, but it does not work:
#Controller
#RequestMapping("/myController")
public class MyController {
#RequestMapping(value = "/test", method = RequestMethod.GET)
public #ResponseBody String myTest() {
FileReader fileReader = null;
BufferedReader bufferedReader = null;
String content = "";
try {
fileReader = new FileReader("file:/WEB-INF/content/somecontent.txt");
bufferedReader = new BufferedReader(fileReader);
content = bufferedReader.readLine();
bufferedReader.close();
}
catch (Exception ignored) {
// ignore
}
return content;
}
}
Any push into the right direction will be highly appreciated :-)
Servlet containers in general, and GAE in particular, won't let you use the File API from within the servlet container like that.
Instead, autowire your controller with the ServletContext, and fetch the resource from that. Also, your exception handling isn't great, you shouldn't ignore exceptions like that, they're there for a reason.
Something like should be OK:
#Controller
#RequestMapping("/myController")
public class MyController {
private #Autowired ServletContext servletContext;
#RequestMapping(value = "/test", method = RequestMethod.GET)
public #ResponseBody String myTest() throws IOException {
InputStream inputStream = null;
try {
inputStream = servletContext.getResourceAsStream("/WEB-INF/content/somecontent.txt");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
return bufferedReader.readLine();
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
}
Alternatively, if you're using Spring 3.x, this might be even simpler:
<mvc:view-controller path="/test" view-name="/WEB-INF/content/somecontent.txt"/>
See docs for what this does, but it may mean you can avoid any code.
The notation "file:" and "classpath:" isn't right with FileReader.
I suggest you to create a FileSystemResource
FileSystemResource resource = new FileSystemResource("/WEB-INF/content/somecontent.txt");
and then to use getFile() or getInputStream() to read file.
This is very useful in a web application, because you can use relative path.
Are you sure you need encoding conversion introduced by reading a file contents via Reader and returning it as String?
If no (i.e. if you want to serve a file as is), you can use request forwarding:
#RequestMapping(value = "/test", method = RequestMethod.GET)
public View myTest() {
return new InternalResourceView("/WEB-INF/content/somecontent.txt");
}
or even (if you have InternalViewResolver):
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String myTest() {
return "forward:/WEB-INF/content/somecontent.txt";
}
If yes, note that you didn't specify encoding when reading file, so that system default encoding is used and behaviour of your application is system-dependent.
Since this is a Spring application, you can rely on it's classes to help with this. In particular, org.springframework.web.context.support.ServletContextResource should prove quite useful:
ServletContextResource resource = new ServletContextResource(servletContext,
"/WEB-INF/content/somecontent.txt");
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(resource.getInputStream()));

Categories