Spring MVC outputStream - spring

my problem is that I have a program which does make a file & stream it out to the user & I have two problem:
1) first how do change the file name & type (x.sql)
2) what if there's an exception while making the file & I want to redirect the user to an error page because my return type of my method is void when we want to write into outputstream in servlet !
my program is like this:
#RequestMapping(value = "/test/{jobid}")
public void dumpData(#PathVariable("jobid") long jobid, HttpServletResponse response) {
try {
response.setContentType("application/octet-stream");
ServletOutputStream out = response.getOutputStream();
String downloadOutput = "";
// AM I ABLE TO SET FILENAME SO THE USER DOWNLOAD THE FILE WITH THAT NAME ?
// DOES SETHEADER HELP ME IN THIS CASE ?
... (making downloadOutput String by values coming from somewhere)
out.write(downloadOutput.getBytes("UTF-8"));
out.flush();
out.close();
} catch(SomeException e){
//WHEN THE RETURN TYPE IS VOID HOW TO REDIRECT USER TO ERROR PAGE IN CASE OF SOME PROBLEM ?
//(INFO:)IF WE DEFINE STRING AS RETURN TYPE THE PROGRAM WILL GET EXCEPTION
}

Answer to your first question:
response.setHeader( "Content-Disposition", "attachment;filename=" + filename );
//for zip file
response.setContentType("application/zip");
Here is the list of mime type for various content types.
I found a solution here. You can change return type to string and return error view name in case of error otherwise return null. What I understood from the link is that returning null and return type void is similar.

What stops you from using response.sendRedirect(errorPage)?

Related

How is HttpServletResponse entangled with the fetch API when making a GET request for a BLOB?

Using Spring Boot, I am trying to implement a REST controller, which can handle a GET request asking to return a BLOB object from my database.
Googling around a little bit, and putting pieces together, I have created the following code snippet:
#GetMapping("student/pic/studentId")
public void getProfilePicture(#PathVariable Long studentId, HttpServletResponse response) throws IOException {
Optional<ProfilePicture> profilePicture;
profilePicture = profilePictureService.getProfilePictureByStudentId(studentId);
if (profilePicture.isPresent()) {
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(profilePicture.get().getPicture());
outputStream.close();
}
}
I am sending the GET request using VanillaJS and the fetch-API:
async function downloadPicture(profilePic, studentId) {
const url = "http://localhost:8080/student/pic/" + studentId;
const response = await fetch(url);
const responseBlob = await response.blob();
if (responseBlob.size > 0) {
profilePic.src = URL.createObjectURL(responseBlob);
}
}
Somehow, this works. That's great, but now I would like to understand the usage of HttpServletResponse in this context, which I am not familiar with. It seems to me that the fetch-API makes use of HttpServletResponse (maybe even creates it), since I am not creating this object or do anything with it.
What is very strange to me is that the return-type of my controller method getProfilePicture() is void, and still I am sending a response, which is most definitely not void.
Also, if the profilePicture was not found in my database, for example due to a non-existing studentId being passed, my controller-method does not do anything. But still, I am getting a response code of 200. That's why I have added the responseBlob.size > 0 part in my Javascript to check for a positive response.
Can someone explain this magic to me, please?
response.getOutputStream(); javadoc says "Returns a ServletOutputStream suitable for writing binary data in the response." It's literally the response stream and you write the picture bytes into it. It's not related to the client reading the response. Alternatively you could just return a byte array which will be automatically written into the response stream and the result will be the same.
To return a different http status code you should change the method return type to ResponseEntity<byte[]>:
#GetMapping("student/pic/studentId")
public ResponseEntity<byte[]> getProfilePicture(#PathVariable Long studentId, HttpServletResponse response) throws IOException {
Optional<ProfilePicture> profilePicture = profilePictureService.getProfilePictureByStudentId(studentId);
if (profilePicture.isPresent()) {
return ResponseEntity.ok(profilePicture.get().getPicture()); //status code 200
} else {
return ResponseEntity.notFound().build(); //status code 404
}
}
ResponseEntity is basically springs way to return different status codes/messages.
Is there a reason why you are manually downloading the image via javascript? You could just create a img element with the http link to the image and the browser will automatically display the image content: <img src="http://localhost:8080/student/pic/studentId">

Image without extension in src not loading in IE alone, and works perfect in all other browers

I have below HTML code:
<img title="hotelThumbImage" id="hotelThumbImage01" width="140px" height="129px"
src="/b2c/images/?url=FixedPkgB2c/FF-252-325"/>
It renders in IE as below:
It renders in all other browser like FireFox and Chrome as:
Related question : How to make a Servlet call form UI which returns the Content itself and place an img tag using Script in the output?
My project is suffering from this too, and it's because IE prevents download/display of files which have a different encoding than their extension. It has something to do with malicious code being able to be hidden as image files simply by changing the extension of the file.
Firefox and Chrome are smart enough to display it as an image so long as the encoding is that of an image, but IE takes no chances, it seems.
You'll have to add the extension that matches your image's encoding for it to display in IE.
Edit: It's also possible that your server is sending the file with a header denoting plain text. Again, Firefox and Chrome are smart enough to handle it, but IE isn't. See: https://stackoverflow.com/a/32988576/4793951
Welcome to IE world... :(
What i would do, in order to have better control of the situation is to modify the getter method, so in Holiday.getPkgCode():
public String getPkgCode() throws IOException {
if (!this.pkgCode.contains(".")) {
String ext = ImgUtil.determineFormat(this.pkgCode);
return this.pkgCode + ImgUtil.toExtension(ext);
} else {
return this.pkgCode;
}
}
To use it you will need to catch exceptions and this ImgUtil class adapted from here:
class ImgUtil {
public static String determineFormat(String name) throws IOException {
// get image format in a file
File file = new File(name);
// create an image input stream from the specified file
ImageInputStream iis = ImageIO.createImageInputStream(file);
// get all currently registered readers that recognize the image format
Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);
if (!iter.hasNext()) {
throw new RuntimeException("No readers found!");
}
// get the first reader
ImageReader reader = iter.next();
String toReturn = reader.getFormatName();
// close stream
iis.close();
return toReturn;
}
public static String toExtension(String ext) {
switch (ext) {
case "JPEG": return ".jpg";
case "PNG": return ".png";
}
return null;
}
}
TEST IT:
NOTE: I placed an image (jpg) without extension placed in C:\tmp folder
public class Q37052184 {
String pkgCode = "C:\\tmp\\yorch";
public static void main(String[] args) throws IOException {
Q37052184 q = new Q37052184();
System.out.println(q.getPkgCode());
}
// the given getter!!!
}
OUTPUT:
C:\tmp\yorch.jpg
You have to set the Content Type property of responses' header in the servlet.
For example in spring 4 mvc,
#GetMapping(value = "/b2c/images/?url=FixedPkgB2c/FF-252-325")
public ResponseEntity<byte []> getImageThumbnail() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(media type));
byte [] content= ...;
return ResponseEntity.ok().headers(headers).body(content);
}

How to upload just an image using Retrofit 2.0

Trying to upload an image and it keeps sending as just bytes, not an image file. This is a very simple call, I don't need to send any params other than the image itself. I don't know how to format logs so I won't post the error here unless requested to.
The service:
public interface FileUploadService {
#Multipart
#POST("upload_profile_picture")
Call<ResponseBody> uploadProfilePicture(#Part("profile_picture") RequestBody file);
}
The call being made (a file is generated earlier, had to remove this code because SO needs the post to be mainly words..dumb..):
// Generate the service from interface
FileUploadService service = ServiceGenerator.createService(FileUploadService.class, this);
// Create RequestBody instance from file
RequestBody requestFile =
RequestBody.create(MediaType.parse("image/*"), imageFile);
Log.d(LOG_TAG, "formed file");
// finally, execute the request
Call<ResponseBody> call = service.uploadProfilePicture(requestFile);
Log.d(LOG_TAG, "sending call");
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call,
Response<ResponseBody> response) {
Log.d(LOG_TAG, "success");
Log.d(LOG_TAG, response.toString());
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d(LOG_TAG, "failure");
Log.e(LOG_TAG, t.getMessage());
}
});
Is the issue with the MediaType.parse method? I've tried "multipart/form-data", "image/jpeg", and the above as well and nothing has worked.
The server team has said they are receiving the call, just as bytes and no image file.
I keep getting a 400 because it's sending all bytes. How can I just send this? Do I need to send as a multipart or what? From what I've seen, you just need to tag the param in the method with #Body and do the above and it should all work. Can anybody tell me why this is happening? Thanks!
This is a known issue in Retrofit 2.
Edit: Support for OkHttp's MultipartBody.Part has been added in the final 2.0 release.
In order to get it working, you need to change your interface a little bit first:
#Multipart
#POST("upload_profile_picture")
Call<ResponseBody> uploadProfilePicture(#Part MultipartBody.Part file);
Then you have to create the Part and make the call like this:
MultipartBody.Part file = MultipartBody.Part.createFormData(
"file",
imageFile.getName(),
RequestBody.create(MediaType.parse("image/*"), imageFile));
Call<ResponseBody> call = service.uploadProfilePicture(file);

Multiple connections on the controller service (Spring)

I have written a controller which takes as a input the domain name , crawls the whole site and gives back the result in JSON format
http://crawlmysite-tgugnani.rhcloud.com/getUrlCrawlData/www.google.com
This gives the data google
http://crawlmysite-tgugnani.rhcloud.com/getUrlCrawlData/www.yahoo.com
This gives data for yahoo
If I try to run these two URL's simultaneously, I see that I am getting the mixed data, and the results of one is affecting the another, even though I try to hit them from different machines.
Here is my controller
#RequestMapping("/getUrlCrawlData/{domain:.+}")
#ResponseBody
public String registerContact(#PathVariable("domain") String domain) throws HttpStatusException, SQLException, IOException {
List<URLdata> urldata = null;
Gson gson = new Gson();
String json;
urldata = crawlService.crawlURL("http://"+domain);
json = gson.toJson(urldata);
return json;
}
What do I need to do modify to allow many multiple independent connections.
Update
Following is my crawl Service
public List<URLdata> crawlURL(String domain) throws HttpStatusException, SQLException, IOException{
testDomain = domain;
urlList.clear();
urlMap.clear();
urldata.clear();
urlList.add(testDomain);
processPage(testDomain);
//Get all pages
for(int i = 1; i < urlList.size(); i++){
if(urlList.size()>=500){
break;
}
processPage(urlList.get(i));
//System.out.println(urlList.get(i));
}
//Calculate Time
for(int i = 0; i < urlList.size(); i++){
getTitleAndMeta(urlList.get(i));
}
return urldata;
}
public static void processPage(String URL) throws SQLException, IOException, HttpStatusException{
//get useful information
try{
Connection.Response response = Jsoup.connect(URL)
.userAgent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.21 (KHTML, like Gecko) Chrome/19.0.1042.0 Safari/535.21")
.timeout(10000)
.execute();
Document doc = response.parse();
//get all links and recursively call the processPage method
Elements questions = doc.select("a[href]");
for(Element link: questions){
String linkName = link.attr("abs:href");
if(linkName.contains(testDomain.replaceAll("http://www.", ""))){
if(linkName.contains("#")){
linkName = linkName.substring(0, linkName.indexOf("#"));
}
if(linkName.contains("?")){
linkName = linkName.substring(0, linkName.indexOf("?"));
}
if(!urlList.contains(linkName) && urlList.size() <= 500){
urlList.add(linkName);
}
}
}
}
catch(HttpStatusException e){
System.out.println(e);
}
catch(SocketTimeoutException e){
System.out.println(e);
}
catch(UnsupportedMimeTypeException e){
System.out.println(e);
}
catch(UnknownHostException e){
System.out.println(e);
}
catch(MalformedURLException e){
System.out.println(e);
}
}
Each of your requests (http://crawlmysite-tgugnani.rhcloud.com/getUrlCrawlData/www.google.com and http://crawlmysite-tgugnani.rhcloud.com/getUrlCrawlData/www.yahoo.com) is processed in a separate thread. You have two instances of the crawlURL() method working simultaneously, but both methods use the same variables (testDomain, urlList, urlMap and urldata). So they mess up each other's data in these variables.
One way to fix the problem is to declare these variables locally (inside the method). This way, new instances of these variables will be created for each invocation of crawlURL(). Alternatively, you can create a new instance of your CrawlService class for each invocation of the crawlURL() method.
Synchronizing threads would be a bad idea here because one requests will wait for another to complete before it can be processed by crawlURL().
As far as SpringMVC is concerned every request running in separate thread. So I think problem is in crawlService which, I suppose, is not stateless (singleton-like). Try to create new crawl service for every request and check if your data is not mixed. If creating crawl service is expensive operation you should rewrite it to work in stateless way.
#RequestMapping("/getUrlCrawlData/{domain:.+}")
#ResponseBody
public String registerContact(#PathVariable("domain") String domain) throws HttpStatusException, SQLException, IOException {
Gson gson = new Gson();
List<URLdata> = new CrawlService().crawlURL("http://"+domain);
return gson.toJson(urldata);
}
I think
urldata = crawlService.crawlURL("http://"+domain);
This call to crawl Service is the one which is affected by Multiple requests coming simultaneously.
check whether crawlService is safe from multithreading.
ie check whether crawlURL() method is synchronized , if not make it synchronized.
or else synchronize the block of calling crawlservice inside controller.

Uploading more than one image

Dear All,
Working on Spring MVC. I want to upload more than one images from the client. How to achieve it. I know how to handle the multipart form data for single image. But now I am expecting some data with some images from the client.
Any help or url that will help me.
Thanks,
Op
Image is also a file. Whether you would be storing it in database / in file system but it is still a file.
In spring MVC, you could do as shown in the below link:
http://viralpatel.net/blogs/spring-mvc-multiple-file-upload-example/
Here are the code i tried and it is working fine at my end.
//Handle multiple images
#RequestMapping(method = RequestMethod.POST, value="upload", consumes=MediaType.MULTIPART_FORM_DATA_VALUE,
produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody JSONResponse uploadImages(HttpServletRequest req)
throws Exception {
try{
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
Set set = multipartRequest.getFileMap().entrySet();
Iterator i = set.iterator();
while(i.hasNext()) {
Map.Entry me = (Map.Entry)i.next();
String fileName = (String)me.getKey()+"_"+System.currentTimeMillis();
MultipartFile multipartFile = (MultipartFile)me.getValue();
System.out.println("Original fileName - " + multipartFile.getOriginalFilename());
System.out.println("fileName - " + fileName);
saveImage(fileName, multipartFile);
}
}
catch(Exception e){
e.printStackTrace();
}
return new JSONResponse();
}

Resources