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
Related
I have a generic.exe file which doesn't contain any users detail in it.
I also have a REST API which takes the userId and returns a File to the client.
Now, what we want to implement in our project is that when someone hits the REST API, we want to take that generic.exe and rename it to manager_userId.exe and return back this "manager_userId.exe".
Points to be noted over here is that:
The generic.exe file should not be modified/deleted at all
When 2 users (userA and userB) hit that same API simultaneously , they should get their own copy of manager_userA.exe and manager_userB.exe
The code what I have written is
#RequestMapping(value = "/downloadExecutable", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON, produces = {MediaType.APPLICATION_OCTET_STREAM})
#ResponseBody
public Response downloadExecutable(#RequestBody DownloadExecutableRequest downloadExecutableRequest,
HttpServletRequest request, HttpServletResponse response) {
File file = downloadExecutable(downloadExecutableRequest, request, response,
getUserID(request), osDetails);
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment;filename=" + file.getName()).build();
}
public File downloadExecutable(DownloadExecutableRequest downloadExecutableRequest, HttpServletRequest request,
HttpServletResponse response, String userId, String osDetails) {
File file = null;
String path = "/home/genericCopy/generic.exe";
synchronized (this) {
BufferedWriter fileWriter = null;
try {
File source = null;
source = new File(path);
Path destination = Paths.get("/tmp/");
Files.copy(source, destination.toFile());
fileWriter = new BufferedWriter(new FileWriter(destination.getFileName().toString()+"_"+userId));
file = new File(destination.getFileName().toString());
} catch (IOException e) {
} finally {
if (fileWriter != null) {
fileWriter.close();
}
}
}
return file;
}
The code is working , but it is creating a temporary file and then renaming it and then returning it back but it will keep on creating the copies of the file for each request.
Is there any smarter way that i can achieve not to create such temporary copies of the user specific files and also handle a scenario when 2 users hit the API simultaneously ?
The name of the file which is downloaded by user has no relationship to the name of the file on disk.
You can just specify any name of the file in the header and the user will see the name.
Specifically, you would just set the filename you want the user to see to the Content-Disposition header and always load the same exe file from the disk.
Something like this:
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment;filename=executable_" + getUserID(request) + ".exe";
You don't need to do any copying in the downloadExecutable function.
You don't need to create a copy of generic.exe file to return it with changed name. You can use correctly parametrised Content-Disposition header, so it would return same file every time, with file name provided by user.
Here you can find example:
#RestController
public class DemoController {
#GetMapping(value = "/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
#ResponseBody
public ResponseEntity downloadExecutable(#RequestParam("userId") String userId) throws IOException {
byte[] file = Files.readAllBytes(Paths.get("/home/genericCopy/generic.exe"));
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_" + userId + ".exe")
.contentLength(file.length)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(file);
}
}
and result of executing this method:
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;
}
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);
I'm looking for a map of all uploaded files in Spring like key => MultipartFile. The ones I've found so far require me to know the key to the file.
Does anyone know a way?
As it turned out, it's so simple:
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public ResponseEntity executeByPost(WebRequest webRequest)
{
MultiValueMap<String, MultipartFile> files = ((StandardMultipartHttpServletRequest) ((ServletWebRequest) webRequest).getRequest()).getMultiFileMap();
return ResponseEntity.ok(files.keySet());
}
You can get collection of Part like this:
#RequestMapping(value = "/loadFile")
#ResponseBody
public String handleTestRequest (MultipartHttpServletRequest r) throws IOException, ServletException {
for (Part part : r.getParts()) {
String name = part.getName();
String result = new BufferedReader(new InputStreamReader(part.getInputStream()))
.lines().collect(Collectors.joining("\n"));
System.out.println(name + " " + result);
}
return "test output";
}
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()));