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()));
Related
I am trying to call a rest api and get the data from the api. I need to add dynamic parameters to the url in spring boot. I am a bit lost as how should I go about it. Can anyone kindly suggest me something?
RestTemplate restTemplate = new RestTemplate();
String consumeJSONString = restTemplate.getForObject("https://maps.googleapis.com/maps/api/geocode/json?latlng=5.47686,-73.961452&key=YOUR_API_KEY"
, String.class);
I would like to append latlng and api key in the url dynamically. I would really appreciate any suggestions.
You have to use the following variation of getForObject method
restTemplate.getForObject(url, responseType, uriVariables);
So it becomes..
String url = "https://maps.googleapis.com/maps/api/geocode/json?latlng={latlng}&key={key}";
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("latlng", "5.47686,-73.961452");
uriVariables.put("key", "YOUR_API_KEY");
String consumeJSONString = restTemplate.getForObject(url, String.class, uriVariables);
As i got a solution to dynamic variable
where this is a Rest Url
#RequestMapping(value = "/studentdetail")
public User detailStudent(#RequestParam(value = "userid", required = true) String userid) throws SQLException { /*your task goes here*/}
and this is what i m sending the dynamic params as userid which can be anything hopefully it will help u a lot
URL url = new URL("http://localhost:9090/testpapers?userid=" + userid);
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
String s1=URLEncoder.encode(postDataParams, "UTF-8");
writer.write(s1);
writer.flush();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
while ((line = reader.readLine()) != null)
{
System.out.println(line);
}
writer.close();
reader.close();
}
If using Spring Framework, we have org.springframework.web.util.UriTemplate which does this in a structured way
Default URI in application.properties
we can define a parametrized uri in properties file. For eg
url.template.google.maps=https://maps.googleapis.com/maps/api/geocode/json?latlng={latitude},{longitude}&key={api_key}
Initialize UriTemplate
The template that we defined in properties file can now be read in a #PostConstruct block and stored in UriTemplate variable. I am saying #PostConstruct just to save trips to fetch the template for each request
#Value("${url.template.google.maps}")
private String googleMapsUri;
private UriTemplate googleMapsServiceUriTemplate;
#PostConstruct
private void init() throws URISyntaxException {
googleMapsServiceUriTemplate= new UriTemplate(googleMapsUri);
}
Expand variables and get URI
Now that we have everything, let's expand and get the uri
Given these values, call below method to get the URI
String latitudeValue = "5.47686";
String longitudeValue = "-73.961452";
String apiKeyValue = "api_key_value";
Below method would take the necessary values, create a map with these values and then expand the uri template to populate the values. We get a nicely structured URI this way.
pubic URI getGoogleMapsServiceUri(latitudeValue, longitudeValue, apiKeyValue) {
Map<String, String> templateVariables = new HashMap<>();
templateVariables.put("latitude", latitudeValue); //could use a variable here
templateVariables.put("longitude", longitudeValue); //could use a variable here
templateVariables.put("api_key", apiKeyValue); //could use a variable here
URI googleMapsServiceUri = googleMapsServiceUriTemplate.expand(templateVariables);
System.out.println("URL is " + googleMapsServiceUri.getPath());
return googleMapsServiceUri;
}
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
In short terms, I simplified the problem a lot. I am calling this code, and the response is received with status 200 (OK):
Receiver.java:
Response response = componentInstanceService.getResource(componentResourceType);
However, I don't know how can I retrieve the String contained in the body from this method:
Sender.java:
#Override
public Response getResource(ComponentResourceType resourceType) {
String path = getPath();
return Response.ok(this.getClass().getResourceAsStream(path)).build();
}
Please note that the communication between classes is working fine, as long as the Response is OK, however, how can I retrieve the String that Response contains?
This is what I would like to do roughly:
Receiver:
String result = componentInstanceService.getResource(componentResourceType);
The documentation for Response makes this pretty clear:
static Response.ResponseBuilder ok(java.lang.Object entity)
Create a new ResponseBuilder that contains a representation.
And:
abstract java.lang.Object getEntity()
Return the response entity.
In other words, the object you passed to Response.ok is the entity. You can retrieve it with the Response’s getEntity() method.
Obviously, you will need to cast it:
Response response = componentInstanceService.getResource(componentResourceType);
InputStream dataSource = (InputStream) response.getEntity();
Then you can read the stream as text. You haven’t mentioned the charset of your text files, so I’ll assume it’s UTF-8:
String result;
try (Scanner scanner = new Scanner(dataSource, StandardCharsets.UTF_8)) {
result = scanner.useDelimiter("\\z").next();
}
Update:
I suspected this might happen. You are returning a raw InputStream, which has no information about what type of data it is.
Change Sender.java to return a DataSource:
#Override
public DataSource getResource(ComponentResourceType resourceType) {
String path = getPath();
return new URLDataSource(this.getClass().getResource(path));
}
This way, the JAX-RS service will not only return HTTP 200 OK, but will also return a Content-Type header corresponding to the intuited type of your file.
You should then be able to invoke the method with:
DataSource dataSource = componentInstanceService.getResource(componentResourceType);
String result;
try (Scanner scanner = new Scanner(dataSource.getInputStream(), StandardCharsets.UTF_8)) {
result = scanner.useDelimiter("\\z").next();
}
There actually is a more robust way to read a DataSource. You can wrap it in a DataHandler:
DataSource dataSource = componentInstanceService.getResource(componentResourceType);
DataHandler handler = new DataHandler(dataSource);
DataFlavor flavor = DataFlavor.selectBestTextFlavor(
handler.getTransferDataFlavors());
if (flavor == null) {
// This should never happen with text files.
throw new IllegalArgumentException(
"Data has no flavors capable of supplying text.");
}
String result;
try (Reader reader = flavor.getReaderForText(handler)) {
StringBuilder s = new StringBuilder();
int c;
while ((c = reader.read()) >= 0) {
s.append((char) c);
}
result = s.toString();
} catch (UnsupportedFlavorException e) {
// Since we started with a flavor provided by the DataHandler,
// we should never get here.
throw new RuntimeException(e);
}
If you want to read the string from the body simply use
String result = componentInstanceService.getResource(componentResourceType).readEntity(String.class);
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.
My goal is to merge/minify all css files and return the result as String.
Here's my Spring test method :
#RequestMapping(value = "/stylesheet.css", method = RequestMethod.GET, produces = "text/css")
#ResponseBody
public void css(HttpServletResponse response) {
File path = new File(servletContext.getRealPath("/WEB-INF/includes/css/"));
File[] files = path.listFiles(...);
for (File file : files) {
InputStream is = new FileInputStream(file);
IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
is.close();
}
}
This is working with Chrome, Firefox and Safari but not with IE and Opera.
After some checks in the inspectors, the URL https://host/project/stylesheet.css is loading in each browsers. I can see the content but it does not seem to be recognized as text/css.
Also, even with produces = "text/css", I can not see the content-type http header in all browsers.
Error log in IE :
CSS ignored because of mime type incompatibility
Does anyone know how to correctly do this?
Working code :
#RequestMapping(value = "/stylesheet.css", method = RequestMethod.GET)
public ResponseEntity<Void> css(HttpServletResponse response) {
response.setContentType("text/css");
File path = new File(servletContext.getRealPath("/WEB-INF/includes/css/"));
File[] files = path.listFiles(...);
for (File file : files) {
InputStream is = new FileInputStream(file);
IOUtils.copy(is, response.getOutputStream());
IOUtils.closeQuietly(is);
}
response.flushBuffer();
return new ResponseEntity<Void>(HttpStatus.OK);
}
I suspect the problem is due to your usage of HttpServletResponse.flushBuffer().
As the API of HttpServletRequest states:
Forces any content in the buffer to be written to the client. A call
to this method automatically commits the response, meaning the status
code and headers will be written.
My assumption would be that Spring attempts to set the Content-Type header on the HttpServletResponse after the method on your controller has returned. However, because you have committed the response with your call to HttpServletResponse.flushBuffer(), it cannot do this.
I would try either:
Injecting the HttpServletResponse into your controller and setting the header yourself in code before calling HttpServletResponse.flushBuffer()
Removing your usage of HttpServletRequest.flushBuffer()
Since you're writing the content directly to the output stream, you don't need to use #ResponseBody. You just need to ensure that you set the Content-Type response header. Also, it'd be better to return a ResponseEntity (rather than void) to indicate to Spring that you're handling the response yourself.
#RequestMapping(value = "/stylesheet.css", method = RequestMethod.GET)
public ResponseEntity css(HttpServletResponse response) {
// Set the content-type
response.setHeader("Content-Type", "text/css");
File path = new File(servletContext.getRealPath("/WEB-INF/includes/css/"));
File[] files = path.listFiles(...);
for (File file : files) {
InputStream is = new FileInputStream(file);
IOUtils.copy(is, response.getOutputStream());
IOUtils.closeQuietly(is);
}
response.flushBuffer();
return new ResponseEntity(HttpStatus.OK)
}