I have a Spring Boot Application with a RestController and a method which will download and pass an image:
#RestController
public class PictureController {
#RequestMapping("/picture/{id}")
public HttpEntity<byte[]> getImage(#PathVariable String id) {
logger.info("Requested picture : >> " + id + " <<");
// !! Execute code for downloading !!
// Create Headers...
// return HttpEntity<byte[]>
}
}
In the logfiles I can read that the method is executed twice and I don't know why.
If I remove the code for downloading it gets executed just once as expected.
Is it because it takes a second to download it?
The code for downloading is...
byte[] response;
try {
URL url = new URL(....);
InputStream in = new BufferedInputStream(url.openStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n = 0;
while (-1 != (n = in.read(buf))) {
out.write(buf, 0, n);
}
out.close();
in.close();
response = out.toByteArray();
I also tried several solutions like...
#RequestMapping(value = "/picture2/{id}", headers = "Accept=image/jpeg, image/jpg, image/png, image/gif")
public #ResponseBody byte[] getArticleImage2(#PathVariable String id) {
I thought maybe a problem with HttpEntity but it's the same behaviour. Works as expected without code for downloading but with downloading an image it gets executed twice.
This is a serious performance issue of my application... :(
What is the problem here?
The problem depends on the Browser which is used to test the RestController.
I'am using Firefox... and Firefox tend to get some html around an image. But the method doesn't return html and so Firefox is starting another request... also for looking for a favicon.
Internet Explorer e.g. doesn't care about it and the method is only executed once as expected!
So my problem is not a real problem because later my image delivered by the RestController will be embedded in a website which has html and a favicon.
Related
My code was initially breaking if I try and push a large file (anything above 1MB size). It is working fine now and able to accommodate the file sizes I want by adding the following in the properties file.
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
But how can I write a proper unit/integration test on this to ensure it allows file size up to 10MB?
The following has a good test example (the accepted answer) but it is using a mock file setup to test.
Using Spring MVC Test to unit test multipart POST request
Is there a way I could mock and specify file size?
Or actually pass in a real large file for testing (preferably not)?
Or a better way to do this, test I can accept a large file up to 10MB?
This is the method to be tested
#PostMapping(path = "/example", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SomeResponse> upload(#PathVariable(#RequestPart("file") MultipartFile file) {
//we won't even get inside thi method and would fail if the file size is over 1MB previously.
// It works currently when I add files with size above 1MB
// cos I added the above 2 lines (spring.servlet.... in the properties file)
// some logic which works fine.
SomeResponse obj = //
return new ResponseEntity<>(obj, HttpStatus.OK);
}
This is current test (and there are other tests to test negative scenarios)
#Test
public void testValidUpload() throws Exception {
String fileContents = "12345";
String expectedFileContents = "12345\nSomeData";
mockServer.expect(requestTo("http://localhost:8080/example"))
.andExpect(method(HttpMethod.POST))
.andExpect(expectFile("file", "test.csv", expectedFileContents))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.TEXT_PLAIN)
.body("done")
);
String response = this.mvc.perform(multipart("/example")
.file(new MockMultipartFile("file", "filename.csv", MediaType.TEXT_PLAIN_VALUE, fileContents.getBytes())))
.andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON))
.andExpect(jsonPath("responseStatusCode", Matchers.equalTo("200")))
.andExpect(jsonPath("httpStatus", Matchers.equalTo("OK")))
.andReturn().getResponse().getContentAsString();
Response response = objectMapper.readValue(response, Response.class);
assertEquals(HttpStatus.OK, response.getHttpStatus());
assertEquals(5, response.id());
}
You can try something like this:
byte[] bytes = new byte[1024 * 1024 * 10];
MockMultipartFile firstFile = new MockMultipartFile("data", "file1.txt", "text/plain", bytes);
See documentation.
You can also refer to this article.
I'm trying to use the ACS (Cisco Secure Access Control System) to manage users in the network equipment using code. I've downloaded the examples provided with the product and built upon that, my code.
When I test it in debug mode, everything works great but when I put my .class files in Jboss Web Server and try to execute the main method from another application, I get this error:
unexpected element (uri:identity.rest.mgmt.acs.nm.cisco.com", local:"user"). Expected elements are (none)
my code is:
private static User getUserByName(RestFacade restFacade, String name)
{
User user = null;
String url = "/Rest/Identity/User/name/";
url = url.concat(name);
HttpResponse response = restFacade.get(url);
try {
byte buffer[] = new byte[8192];
int read = 0;
StringBuffer responseBody = new StringBuffer();
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream content = response.getEntity().getContent();
while ((read = content.read(buffer)) != -1) {
responseBody.append(new String(buffer, 0, read));
}
}
ByteArrayInputStream bais = new ByteArrayInputStream(responseBody
.toString().getBytes());
Unmarshaller unmarshaller = JAXBContext.newInstance(User.class).createUnmarshller();
user = (User) unmarshaller.unmarshal(bais);
} catch (Exception e) {
e.printStackTrace();
}
return user;
}
Almost every solution I found for such an error, said to change something in the Rest method I'm trying to invoke or change the xsd, but I don't have access to those..
The weird thing is that it works when I debug from Eclipse but not on runtime. Maybe there's a different version to JAXB I'm using? How can I be sure which jar loads up?
Thank you,
Igor.
That sounds like your development environment you use to debug and your target application server have a different version of either the JAXB classes / library and/or the used XSDs.
The documentation (http://www.cisco.com/c/en/us/td/docs/net_mgmt/cisco_secure_access_control_system/5-3/sdk/acs_sdk/rest.html) specifies the three XSDs (Common, Identity and Query).
Have you verified the version you have (or have downloaded) match the generated JAXB classes on the target application server?
My HTML file has the following command
<img src="/5/background" alt="" width="192" height="192">
Which I have bound to the following inside a #RestController in a Spring Boot application:
#RequestMapping(value = "/{connector}/background", method = RequestMethod.GET)
public File getConnectorBackgroundImage(#PathVariable("connector") int connector)
{
File image = new File("folder" + connector + "/myPicture");
return image;
}
I have done my debugging and I know the method is being entered, and I know the path is correct, but all that shows is the icon in the browser when there is a problem loading a picture.
What else am I missing?
Spring does not know how to handle the file that way. If you return a File, the controller will simply show you the path to the file if you call the REST API.
The proper way to do it is to read the file as a byte[]. Using commons-io you could come up with something like this:
File image = new File("folder" + connector + "/myPicture");
FileInputStream input = null;
try {
input = new FileInputStream(image);
return IOUtils.toByteArray(input);
} finally {
IOUtils.closeQuietly(input);
}
Another thing you shouldn't forget is to provide the mimetype. To do that you tweak the #RequestMapping annotation a bit by providing the produces property:
#RequestMapping(value = "/{connector}/background", method = RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
This should do the trick.
EDIT: Didn't notice the comments, you already fixed it by yourself.
I'm working on spring mvc file upload. in the view, i use plupload multiple file upload plugin. here is my upload action:
#RequestMapping(value = CrudURI.uploadDo, method = RequestMethod.POST, produces = "application/json")
public #ResponseBody String uploadDo(#RequestBody MultipartFile file,
#RequestParam String name,
#RequestParam(required=false, defaultValue="-1") int chunks,
#RequestParam(required=false, defaultValue="-1") int chunk) {
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(new File("/uploads/"+name)));
stream.write(bytes);
stream.close();
} catch (Exception e) {
}
} else {
}
System.err.println(file.getOriginalFilename());
return null;
}
this action is working without any error or exception, but the uploaded file not saved. what's the problem?
thanks
Simply try following code:
File fileToSave=new File("/uploads/"+name);
file.transferTo(fileToSave);
And also make sure that you are actually getting file in mapping.
Darshan solution is correct, your original code has an issue that you're reading a file as getBytes() which is not using a buffer, but your writing using a buffer.
I think that your issue is most likely that you're trying to overwrite the uploaded file, at least it appears so based on your output location "/uploads/"+name. Try changing the name, and instead of having an empty catch block, add log for your exception it will point you to the issue
I'm unable to save a Data URI in JSP. I am trying like this, is there any mistake in the following code?
<%# page import="java.awt.image.*,java.io.*,javax.imageio.*,sun.misc.*" %>
function save_photo()
{
Webcam.snap(function(data_uri)
{
document.getElementById('results').innerHTML =
'<h2>Here is your image:</h2>' + '<img src="'+data_uri+'"/>';
var dat = data_uri;
<%
String st = "document.writeln(dat)";
BufferedImage image = null;
byte[] imageByte;
BASE64Decoder decoder = new BASE64Decoder();
imageByte = decoder.decodeBuffer(st);
ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
image = ImageIO.read(bis);
bis.close();
if (image != null)
ImageIO.write(image, "jpg", new File("d://1.jpg"));
out.println("value=" + st); // here it going to displaying base64 chars
System.out.println("value=" + st); //but here it is going to displaying document.writeln(dat)
%>
}
}
Finally, the image is not saved.
I think you didn't get the difference between JSP and JavaScript. While JSP is executed on the Server at the time your browser requires the web page, JavaScript is executed at the Client side, so in your browser, when you do an interaction that causes the JavaScript to run.
You Server (eg Apache Tomcat) will firstly execute your JSP code:
String st = "document.writeln(dat)";
BufferedImage image = null;
byte[] imageByte;
BASE64Decoder decoder = new BASE64Decoder();
imageByte = decoder.decodeBuffer(st);
ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
image = ImageIO.read(bis);
bis.close();
if (image != null)
ImageIO.write(image, "jpg", new File("d://1.jpg"));
out.println("value=" + st);
System.out.println("value=" + st);
As you can see, nowhere is the value of st changed. Your broser will receive the following snippet from your server:
value=document.writeln(dat);
Since your browser is the one that executes JavaScript, he will execute it and show the Base64-encoded Image - but your server won't.
For the exact difference, read this article.
To make the code working, the easiest way is to redirect the page:
function(data_uri)
{
// redirect
document.location.href = 'saveImage.jsp?img='+data_uri;
}
Now, you can have a JSP-page called saveImage.jsp that saves the Image, and returns the webpage you had already, and write the dara_uri into the element results.
Another, but more difficult way is to use AJAX. Here is an introduction to it.
You are trying to use JavaScript variables in Java code. Java code is running on your server, while Javascript code runs in user's browser. By the time JavaScript code executes, your Java code has already been executed. Whatever you're trying to do, you have to do it in pure javascript, or send an AJAX call to your server when your Javascript code has done it's thing.