How to serve sitemap.xml from outside project folder in Spring Boot 2 - spring-boot

I have a Spring Boot 2 Web application which is a blogging website, where I have a dynamic sitemap.xml file. The sitemap.xml gets updated whenever we add a new article in the repository. Now I need to serve the sitemap.xml file from outside of the project location. Consider the sitemap.xml is present in the following location:
/home/admin/sitemap.xml
My requirement is, whenever a user hit the url https://<my_url>/sitemap.xml then the file should be served from the mentioned location. I have added the mapping for the same.
#Configuration
#AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
public class WebConfiguration implements WebMvcConfigurer {
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/sitemap.xml/**").addResourceLocations("/home/admin/sitemap.xml");
}
}
But the code is not working. Can anyone suggest that, what I am doing wrong here.

Finally, I have solved this by writing a GetMapping. Many solutions on the internet are saying that to return the sitemap.xml file as a String. But please note Google will not read that sitemap.xml file for the wrong Content-Type header. The Content-Type must be "application/xml". Following is my #GetMapping
#GetMapping(value = "/sitemap.xml")
public void serveSiteMapFile(HttpServletResponse response) throws IOException {
response.setContentType("application/xml");
try (BufferedReader bufferedReader =
new BufferedReader(new FileReader(new File("/home/admin/sitemap.xml")))) {
String line;
StringBuilder siteMapBuilder = new StringBuilder();
while ((line = bufferedReader.readLine()) != null) {
siteMapBuilder.append(line);
}
ServletOutputStream outStream = response.getOutputStream();
outStream.println(siteMapBuilder.toString());
outStream.flush();
outStream.close();
}
}

Related

Testing file upload in Spring Boot leads to FileUploadException (multipart boundary was not set)

I'm trying to upload files to my Spring Boot application and directly writing them to their destination (not in a temp file first). The application code I have works, but I can't get my unit test to work. My controller looks like this:
#PostMapping("/upload")
#ResponseBody
public String handleFileUpload(final HttpServletRequest request) throws IOException {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
throw new ResponseStatusException(BAD_REQUEST, "Input was not of type multipart");
}
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator fileIterator = upload.getItemIterator(request);
while (fileIterator.hasNext()) {
FileItemStream item = fileIterator.next();
if (!item.isFormField()) {
// Save the file
try {
return myFileStorageService.store(item.openStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
throw new ResponseStatusException(BAD_REQUEST, "Input did not contain a file");
}
This code works great, but my test doesn't:
#MockBean
private MyFileStorageService myFileStorageService;
#Autowired
private MockMvc mockMvc;
#Test
void shouldUploadFile() throws Exception {
final InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("testfile.txt");
final MockMultipartFile testFile = new MockMultipartFile("file", "testfile.txt", null, inputStream);
doReturn("success!").when(myFileStorageService).store(testFile);
mockMvc.perform(multipart("/upload").file(testFile))
.andExpect(status().isOk())
.andExpect(content().string("success!"));
verify(myFileStorageService).store(testFile);
}
This results in the following exception:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.init(FileItemIteratorImpl.java:189)
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.getMultiPartStream(FileItemIteratorImpl.java:205)
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.findNextItem(FileItemIteratorImpl.java:224)
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.<init>(FileItemIteratorImpl.java:142)
at org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:252)
at org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload.getItemIterator(ServletFileUpload.java:134)
at com.lolmewn.FileUploadController.handleFileUpload(FileUploadController.java:128)
...
And in my config, I have configured the following:
spring:
servlet:
multipart:
enabled: false
max-file-size: -1
max-request-size: -1
I expect Spring would generate the multipart boundaries for me, just like the browser or Postman do, is this not the case? I saw many similar questions, with most of them explicitly setting their content-type as the primary error, but as far as I know I'm not setting a content-type anywhere, so I expect Spring to generate it for me.
If you are using default application.properties, then add #SpringBootTest annotation at top of your class which will instantiate it. If using something like application-test.properties you need to include #ActiveProfiles(test)
as well.
If you are using a config class to represent it
#EnableConfigurationProperties(value = YourConfig.class)
EDIT: Change
final MockMultipartFile testFile = new MockMultipartFile("file", "testfile.txt", null, inputStream);
To
final MockMultipartFile testFile = new MockMultipartFile("file", "testfile.txt",
MediaType.MULTIPART_FORM_DATA_VALUE, inputStream);

Transferring big files in spring integration

The spring integration flow I wrote has to get files (some of them are as big as 4G) from a rest service and transfer them to a remote shared drive. For downloading them from the rest service I configured this simple component:
#Bean
public HttpRequestExecutingMessagehandler httpDownloader (RestTemplate template){
Expression expr = (new SpelExpressionParser()).parseExpression("payload.url");
HttpRequestExecutingMessagehandler handler = new HttpRequestExecutingMessagehandler (expr, template);
handler.setExpectedResponseType(byte[].class);
handler.setHttpMethod(GET);
return handler;
}
Unfortunately this won't scale meaning for larger files it will eventually throw java.lang.OutOfMemoryError: Java heap space, even if i add more memory with -Xmx or -XXMaxPermSize
So my question is, what to do in order to avoid these problems no matter how big the files will be?
I think I have answered you in some other similar your question that Spring RestTemplate is not designed for streaming response body. It is explained in this SO thread: Getting InputStream with RestTemplate.
One of the solution which may work for your is to write a custom HttpMessageConverter which would return a File object containing data from HTTP response. This article explains how to do that with the ResponseExtractor, but something like FileHttpMessageConverter is not so hard to implement based on experience from that article. See StreamUtils.copy(InputStream in, OutputStream out)
Then you inject this FileHttpMessageConverter into your HttpRequestExecutingMessagehandler - setMessageConverters(List<HttpMessageConverter<?>> messageConverters).
Your service for remote shared drive should already deal with this local temporary file to get that large content without consuming memory.
See also this one about possible approach via WebFlux: https://www.amitph.com/spring-webclient-large-file-download/
Created this starting from ByteArrayHttpMessageConverter class and injected it into the custom RestTemplate I use. But this solution is based on using a File message, which is not quite the streaming I was hoping for.
public class FileCustomConverter extends AbstractHttpMessageConverter<File> {
public FileCustomConverter() {
super(new MediaType[]{MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL});
}
public boolean supports(Class<?> clazz) {
return File.class == clazz;
}
public File readInternal(Class<? extends File> clazz, HttpInputMessage inputMessage) throws IOException {
File outputFile = File.createTempFile(UUID.randomUUID().toString(), ".tmp");
OutputStream outputStream = new FileOutputStream(outputFile);
StreamUtils.copy(inputMessage.getBody(), outputStream);
outputStream.close();
return outputFile;
}
protected Long getContentLength(File bytes, #Nullable MediaType contentType) {
return bytes.length();
}
protected void writeInternal(File file, HttpOutputMessage outputMessage) throws IOException {
InputStream inputStream = new FileInputStream(file);
StreamUtils.copy(inputStream, outputMessage.getBody());
inputStream.close();
}
}

Spring Controller return static HTML site from any directory

I would like to return in my #Controller static HTML website that was generated by other process. Let's say that generated .html files are in /tmp/generated. I'm trying to read file and pass its content to ResponseEntity:
#GetMapping(value = "test")
ResponseEntity<String> test(#RequestParam("filename") String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("/tmp/generated/" + filename)), "UTF-8");
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity<String>(content, headers, HttpStatus.OK);
}
But when I open url in browser I get badly encoded html content (stating and ending with '"'):
"\u003chtml\u003e\n\u003chead\u003e\n \u003cmeta charset\u003d\"utf-8\" /\u003e\n \u003cmeta http-equiv\u003d\"X-UA-Compatible\" content\u003d\"IE\u003dedge\" /\u003e\n \u003cmeta name\u003d\"viewport\" content\u003d\"width\u003ddevice-width, initial-scale\u003d1\" /\u003e [.....]
If I add produces = MediaType.TEXT_HTML_VALUE to my #GetMapping annotation then I get 406 Not Acceptable error response (but no exception in my spring app)...
How to fix it?
I'm not sure why you are facing problems when using produces in your mapping.
I gave a quick try and it worked for me.
#GetMapping(value = "test", produces=MediaType.TEXT_HTML_VALUE)
public ResponseEntity<String> test(#RequestParam("filename") String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("/tmp/generated/" + filename)), "UTF-8");
return new ResponseEntity<String>(content, HttpStatus.OK);
}
Tested in Chrome browser:
File
NOTE: I tested this controller using SpringBoot v2.0.5.RELEASE
Cheers!
I have successfully build application with Spring Boot 1.5.2.RELEASE and it will return static HTML site from any directory
you can checkout here

loading dynamic image from local source

I have saved some images to local path of pc and saved the file name to db. Now i want to load all the image from db and show to html page using java script and thymemleaf. Browser says not allowed to load local resources , So i made a servlet in spring boot. But it does not showing image.
Servlet
#WebServlet("/admin/imgServlet")
public class AdminImageServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String urlImage = request.getParameter("admin");
response.setContentType("image/jpeg");
ServletOutputStream out;
out = response.getOutputStream();
FileInputStream fin = new FileInputStream("C:/Users/Public/Documents/" + urlImage);
BufferedInputStream bin = new BufferedInputStream(fin);
BufferedOutputStream bout = new BufferedOutputStream(out);
int ch = 0;
while ((ch = bin.read()) != -1) {
bout.write(ch);
}
bin.close();
fin.close();
bout.close();
out.close();
}
}
#Bean
public ServletRegistrationBean adminImageServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(AdminImageServlet.class);
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/admin/imgServlet");
servletRegistrationBean.setName("imgServlet");
return servletRegistrationBean;
}
Java script for loading image
'<img src=imgServlet?admin='+articles.bannerImg+' class="img-responsive" />'
error i'm getting imgServlet:1 GET http://localhost/techtalks/admin/imgServlet?admin=pyjc4x0B.jpeg 404 (Not Found)
And i also want to load image from local resources to thymeleaf html page
model.addAttribute("localImg", "E:\\picz\\assasins.jpg"); spring controller will pass the Model like this and it will pass the image to respective file to html page like this.
<img th:src="#{${localImg}}"/>
Your Servlet code works for me. I am able to get the image successfully.
I presume the problem could be most likely the file name. Please input the file name as below (i.e. without 'e' in jpeg).
It should work if the file is present in the local path that you are referring.
pyjc4x0B.jpg

Is it possible to get around the Cross-Origin limitation when calling the Google Places API Web service?

Is it possible enable the Google Placess API WebService to allow cross-origin requests from my domain so that I can access the service directly from the browser? I'v been experimenting with the API-keys by creating a Browser API key and then adding my domain to the referers list, but to no avail. Not sure if that is what the refeferer property is for anyway.
Is this limitation by design, or am I missing something here?
Google Places API WebService is the service that I want to use. Neither the Places Autocomplete or Places Search in the Places Library are suitable for my particular requirement.
Cheers
Stian
This is a client-side based limitation, so the short answer is: no.
However there are websites and services that try to surmount this problem by using scripts (loading them on the fly).
Have a look here and here (these articles are about generic cross-domain AJAX requests)
The Places-API is also available inside the Maps-Javascript-API, you don't need to struggle with cross-origins there.
Let me say its impossible to get around .I tried using java instead the code just works for http request(i used here is for graph.facebook.com):
public class search {
private static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
public static JSONObject readJsonFromUrl(String url) throws IOException, JSONException {
InputStream is = new URL(url).openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JSONObject json = new JSONObject(jsonText);
return json;
} finally {
is.close();
}
}
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
System.getProperties().put("http.proxyHost", "172.16.0.2");
System.getProperties().put("http.proxyPort", "8080");
JSONObject json = readJsonFromUrl("http://maps.googleapis.com/maps/api/place/nearbysearch/json?key=AIzaSyBRlMLIdoTk-j4OZCucR47rVMLhMmvZVRw&type=hospital&location=12.8213125%2C80.0442&radius=500&_=1427359809583");
System.out.println(json.toString());
// System.out.println(json.get("about"));
// System.out.println("hello ");
}
}
If you replace the link with places api web search it will not work,the reason is that google does not give its services on HTTP domain,,, and my code only works on HTTP domain(not HTTPS)

Resources