Not able to read Multipart file using spring controller when file passed via apache httpclient - apache-commons-httpclient

On my UI page, I am trying to upload one file by setting the enctype="multipart/form-data" and encoding="multipart/form-data" on my html form.
Able to read the file contents successfully in my server class (servlet) using the org.apache.commons.fileupload.servlet.ServletFileUpload APIs.
After that I am trying to pass the file or its contents to another server using the apache commons Httpclient using its muiltpart option, I can verify the contents are being passed to another layer (by checking the request body contents or verifying the request in chrome developer tool)
But on another server layer (which is spring based controller),when trying to read the uploaded file using to be uploaded via but not getting the contents. Rather it says "Required MultipartFile parameter 'fileContents' is not present".
Could you please help me out what could be the possible issue for not getting file in Spring controller.
Server Class / servlet Implementation for posting the file to different server:
HttpMethod httpMethod = new PostMethod(epsURL);
String contentTypeRequested = request.getContentType();
httpMethod.setRequestHeader("Content-type", contentTypeRequested);
if(isMultipart){
String content = getUploadFileContents(request);
File file = null;
try {
file = new File("fileContents");
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw;
fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content.toString());
bw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
try{
Part[] parts = {
new FilePart(file.getName(), file)
};
MultipartRequestEntity multipart = new MultipartRequestEntity(parts, httpMethod.getParams());
((PostMethod) httpMethod).setRequestEntity(multipart);
}catch(Exception e){
e.printStackTrace();
}
}
2.Spring layer changes inside context-config.xml:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000"/>
<property name="maxInMemorySize" value="100000"/>
</bean>
3.Spring Controller Implementation:
#RequestMapping(method = RequestMethod.POST, value = "/type/details")
public
void getTypeDetails(
#RequestParam("fileContents") MultipartFile file,
HttpServletRequest httpRequest) {
/// some business logic here based on file object.
}
I am getting the below error:
Error:
2014-04-16 16:28:51,638 [http-bio-8080-exec-2] ERROR com.MyControllerImpl - Exception Occured: : org.springframework.web.bind.MissingServletRequestParameterException: Required MultipartFile parameter 'fileContents' is not present
at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:208)

I find out that if you want to get Multipart file in your controller without its name, you can try something like this:
#RequestMapping(method = RequestMethod.POST, value = "/type/details")
public
void getTypeDetails( any other parameters,
MultipartRequest request) {
/// some business logic here based on file object.
}
The MiltipartRequest will contain all information about multipart request section.

Related

Spring - How to stream large multipart file uploads to database without storing on local file system [duplicate]

This question already has answers here:
SpringBoot: Large Streaming File Upload Using Apache Commons FileUpload
(5 answers)
Closed 2 years ago.
Spring boot's default MultiPartResolver interface handles the uploading of multipart files by storing them on the local file system. Before the controller method is entered, the entire multipart file must finish uploading to the server.
We are storing all of our uploaded files directly to a database and our servers have a very small disk quota, so if a large file is uploaded, we are seeing an IOExeption - Disk quota exceeded.
Is there a way to get the stream directly from the client's incoming request before Spring's MultiPartResolver stores the file on the local filesystem so the we can stream directly to our db?
You could use apache directly, as described here https://commons.apache.org/proper/commons-fileupload/streaming.html.
#Controller
public class UploadController {
#RequestMapping("/upload")
public String upload(HttpServletRequest request) throws IOException, FileUploadException {
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iterator = upload.getItemIterator(request);
while (iterator.hasNext()) {
FileItemStream item = iterator.next();
if (!item.isFormField()) {
InputStream inputStream = item.openStream();
//...
}
}
}
}
Make sure to disable springs multipart resolving mechanism.
application.yml:
spring:
http:
multipart:
enabled: false
Actually it is not trivial task. If you would like to write stream from client right to the database, you have to process request manually. There are some libraries, that can make this task simpler. One of them is "Apache Commons FileUpload". Below very simple example, how can you process incoming multipart/form-data request by this library.
#Controller
public class Controller{
#RequestMapping("/upload")
public String upload(HttpServletRequest request){
String boundary = extractBoundary(request);
try {
MultipartStream multipartStream = new MultipartStream(request.getInputStream(),
boundary.getBytes(), 1024, null);
boolean nextPart = multipartStream.skipPreamble();
while(nextPart) {
String header = multipartStream.readHeaders();
if(header.contains("filename")){
//if input is file
OutputStream output = createDbOutputStream();
multipartStream.readBodyData(output);
output.flush();
output.close();
} else {
//if input is not file (text, checkbox etc)
ByteArrayOutputStream output = new ByteArrayOutputStream();
multipartStream.readBodyData(output);
String value = output.toString("utf-8");
//... do something with extracted value
}
nextPart = multipartStream.readBoundary();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private String extractBoundary(HttpServletRequest request) {
String boundaryHeader = "boundary=";
int i = request.getContentType().indexOf(boundaryHeader)+
boundaryHeader.length();
return request.getContentType().substring(i);
}
}
Header for file field will looks like:
Content-Disposition: form-data; name="fieldName"; filename="fileName.jpg"
Content-Type: image/jpeg
Header for simple field will looks like:
Content-Disposition: form-data; name="fieldName";
Note, that this snippet is just simplified example to show you direction. There is no some details like: extract field name from header, create database output stream etc. You can implement all of this stuff by your own.
Examples of multipart request's field headers you can find in RFC1867. Information about multipart/form-data RFC2388.

Spring returning image as ResponseEntity<byte[]> - image corrupt

I am working on a spring 3.2.7 app and it sends signatures stored in the database as base64 string back to the users browser via a spring controller which outputs the byte array ResponseEntity.
The image is always corrupted and I havent worked on this part of the system as I double checked in svn and the controller has not been touched since the branch I am working on was created.
I am able to convert the base64 string to an image on my desktop and I am also able to the convert the byte array returned to browser into an image before spring steps in.
Below is my code, this was apparently working before so perhaps there is some config change that could cause this?
#RequestMapping(value = "/submissions/signature/{type}/{id}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<byte[]> getSignature(#PathVariable String type, #PathVariable Integer id) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String base64 = ... gets from db
byte[] bytes = Base64.decodeBase64(base64);
BufferedImage bi = ImageIO.read(new ByteArrayInputStream(bytes));
ImageIO.write(bi, "png", baos);
HttpHeaders headers = new HttpHeaders();
headers.setLastModified(Calendar.getInstance().getTime().getTime());
headers.setCacheControl("no-cache");
headers.setContentType(MediaType.IMAGE_PNG);
headers.setContentLength(baos.toByteArray().length);
//Image as base64 string is ok in converter
System.out.println("BASE 64 IMAGE IS: " + base64);
//This image is created ok on desktop
FileOutputStream fos = new FileOutputStream("C:\\Users\\p\\Desktop\\test_signature.png");
fos.write(bytes);
fos.close();
//This image is created ok on desktop
FileOutputStream fos3 = new FileOutputStream("C:\\Users\\p\\Desktop\\test_signature_baos.png");
fos3.write(bytes);
fos3.close();
return new ResponseEntity<byte[]>(baos.toByteArray(), headers, HttpStatus.OK);
}
The image is being rendered in the browser like:
<img id="userSignature" width="296" height="110" style="border:0px" src="/webapp/service/submissions/signature/user/${subid}" alt="User signature" />
I have not changed this class and I am told that it did work, I am able to create images from both byte arrays and they are ok and looks the same and I able to render the signature string ok for testing like:
<IMG SRC="data:image/png;base64, <base_64_string>" ALT="">
has anyone experienced similar issues or know what could be causing this?
I have now tried sending an image from my file system already created as png and that also fails.
I have now noticed that CSV files do not download properly in the app and they stream in the same way:
#RequestMapping(value = "/results/csv", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<byte[]> getResultsInCsvFormat() throws IOException {
I have successfully returned file contents with the help of an InputStream:
#RequestMapping(value = "/submissions/signature/{type}/{id}",
method = RequestMethod.GET)
public HttpEntity getFile(HttpServletResponse response,
#PathVariable String type,
#PathVariable Integer id) {
String base64 = "foo"; // get base-64 encoded string from db
byte[] bytes = Base64.decodeBase64(base64);
try (InputStream inputStream = new ByteArrayInputStream(bytes)) {
StreamUtils.copy(inputStream, response.getOutputStream());
response.setContentType(MediaType.IMAGE_PNG_VALUE);
} catch (IOException e) {
// handle
}
return new ResponseEntity(HttpStatus.OK);
}
Note that I'm not using ResponseBody, and in my working version I am using MediaType.APPLICATION_OCTET_STREAM_VALUE rather than the actual file content type.
The accepted solution didn't work for me in Spring Boot 2.x for Base64 images. Here is how I returned a Base64 image:
#GetMapping(value = "/pixels/{id}", produces = MediaType.IMAGE_PNG_VALUE)
#ResponseBody
public byte[] pixelTracking(#PathVariable String id) {
// TODO: do whatever you want here
// return png image
String base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=";
return Base64.getDecoder().decode(base64);
}
Ok so I have fixed this now with some thanks to beerbajay who showed me that downloading by streaming to the response directly was ok and also that I should look at ByteArrayHttpMessageConverter.
It turned out that I had made a mistake in the spring config and I realised this after reading the spring documentation that told me that ByteArrayHttpMessageConverter is registered automatically when using <mvc:annotation-driven />.
The mvc annotation driven tag had been removed from the config as I thought this was doing the same thing (and I thought it only needed to be declared once within a spring context):
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
It looks like this alone isnt sufficient to set up annotation-driven in the app and I have added back in the tag to the context.xml file.
I dont understand why this needs to be in two places though as all the xml config is loaded through same xml file in what I assumed was the same spring context:
<import resource="config/properties.xml" />
<import resource="config/security.xml" />
<import resource="config/context.xml" />
<import resource="config/content-negotiation.xml" />
<import resource="config/rest-client.xml" />
my solution is :
backend is spring boot and frontend is reactjs
bug is different syntax is backend and frontend : java use ("_","-") and web(reactjs,...) use ("/","+") |
for example : "PGjQOA66-_ne-" convert to "PGjQOA66+//ne/+"
you can testing base64 in this link :https://onlinepngtools.com/convert-base64-to-png
back end code :
ByteArrayOutputStream baos = new ByteArrayOutputStream();
File file = new File(path);/// path : is external directory and local directory in server
FileInputStream imageInFile = new FileInputStream(path);
byte imageData[] = new byte[(int) file.length()];
imageInFile.read(imageData);
String base64 = encodeImage(imageData);
/// 1. Not show if size up 300KB !!! :|
/// 2. base64.replaceAll("_","/").replaceAll("-","+")
byte[] bytes = Base64.decodeBase64(base64.replaceAll("_","/").replaceAll("-","+"));
BufferedImage bi = ImageIO.read(new ByteArrayInputStream(bytes));
ImageIO.write(bi, "png", baos);
HttpHeaders headers = new HttpHeaders();
headers.setLastModified(Calendar.getInstance().getTime().getTime());
headers.setCacheControl("no-cache");
headers.setContentType(MediaType.IMAGE_PNG);
headers.setContentLength(baos.toByteArray().length);
return new ResponseEntity<byte[]>(baos.toByteArray(), headers, HttpStatus.OK);
and other solution for back end:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
File file = new File(path);/// path : is external directory file and local directory file in server, or path get of database.
FileInputStream imageInFile = new FileInputStream(path);
byte imageData[] = new byte[(int) file.length()];
imageInFile.read(imageData);
/// 1. Not show if size up 300KB !!! :| i dont now!
/// 2. base64.replaceAll("_","/").replaceAll("-","+")
String base64 = encodeImage(imageData).replaceAll("_","/").replaceAll("-","+");
return base64;
and reactjs code is :
const [imageData, setImageData] = React.useState({});
setImageData(...request to backend);
<img src={`data:image/png;base64,${imageData}`} alt="This Is alert" />

SPRING MVC 3 - NOT displaying image in JSP

I have a controller serving images from an external directory (say c:\images\userID\photo.png), and this controller does its job very well. However, the img tag in my JSP file shows the image icon instead of the image returned by this controller.
Here's my controller:
#RequestMapping(value = "/load/{imageId}/", method = RequestMethod.GET)
public ResponseEntity<byte[]> loadImage(#PathVariable("imageId") Long imageId, HttpServletRequest request)
{
final org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
BufferedImage image;
Photo photo = photoManager.getSinglePhoto(imageId);
headers.setContentType(MediaType.IMAGE_PNG);
try
{
if (photo == null)
{
File defaultFile = new File("c:/images/default.png");
image = ImageIO.read(defaultFile);
return new ResponseEntity<byte[]>(((DataBufferByte)image.getData().getDataBuffer()).getData(), headers, HttpStatus.CREATED);
}
File file = new File(photo.getPath());
image = ImageIO.read(file);
return new ResponseEntity<byte[]>(((DataBufferByte)image.getData().getDataBuffer()).getData(), headers, HttpStatus.CREATED);
}
catch (IOException ex)
{
return new ResponseEntity<byte[]>(null, headers, HttpStatus.NOT_FOUND);
}
}
I have found reading other answers here, that I need to include messageConverters in my application context, and I did it.
Here's a portion of my application-context.xml
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list>
<bean id="byteArrayMessageConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
</util:list>
</property>
</bean>
The eclipse XML editor complains about MethodHandlerAdapter being deprecated.
JSP:
<img src="/mavenspringapp/photo/load/131/" width="128" height="128" alt="laf02.jpg">
Why isn't the image getting displayed even when the controller sends the response correctly (201). Thanks in advance.
A little bit of googling tells me that HTTP 201 means created.
If the image exists, why are you sending a response code telling the client you just created the image?
I don't know for sure how web browsers handle it, but perhaps try changing your response codes to 200, as you aren't really creating anything.
The problem was in the controller method. Apparently loading the image the way I did it, is not correctly done. So I modified my method as shown below:
#RequestMapping(value = "/load/{imageId}/", method = RequestMethod.GET)
public ResponseEntity<byte[]> loadImage(#PathVariable("imageId") Long imageId, HttpServletRequest request)
{
final org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
Photo photo = photoManager.getSinglePhoto(imageId);
headers.setContentType(MediaType.IMAGE_PNG);
try
{
if (photo == null)
{
File defaultFile = new File("c:/images/default.png");
byte[] content = FileUtils.readFileToByteArray(defaultFile);
return new ResponseEntity<byte[]>(content, headers, HttpStatus.OK);
}
File file = new File(photo.getPath());
byte[] content = FileUtils.readFileToByteArray(file);
return new ResponseEntity<byte[]>(content, headers, HttpStatus.OK);
}
catch (IOException ex)
{
return new ResponseEntity<byte[]>(null, headers, HttpStatus.NOT_FOUND);
}
}
And now it works great! I hope this helps somebody else. Thank you all for your replies.
You may consider the alternative instead of controller displaying the images you can directly access images your jsp for this you need to put the the mapping information in your spring configuration xml like
<mvc:resources mapping="/image/**" location="file:///D:/images/" /> and in your jsp file you can directly can directly call
<img src="<spring:url value='/images/logo.png'/>" /> and make sure your have mentioned the spring tag in your jsp
<%#taglib uri="http://www.springframework.org/tags" prefix="spring"%>

Spring MVC with ajax file upload and MultipartFile

I have an issue using Ajax upload with Spring 3 MVC. I understand that I have to configure multipartResolver bean in spring config, which I've done. Than I can have controller like this
#RequestMapping(value ="/settingsSim")
#ResponseBody
public Map uploadSimSettings(#RequestParam(value="qqfile", required=true) MultipartFile settings) {
Map<String, Object> ret = new HashMap<String, Object>();
return ret;
}
The problem is that when I actually send the request to the server (actually valums Ajax file upload does this for me), I get an Internal server error response and nothing is shown in the logs. I am really scratching my head now, as I cannot figure out the problem.
my solution:
#RequestMapping(value = "/create/upload", method = RequestMethod.POST, consumes="multipart/form-data", produces="application/json")
#ResponseBody()
public String handleImageUpload(#RequestParam(value="qqfile", required=true) MultipartFile[] files,
#ModelAttribute(value="files") List<MultipartFile> filesSession) throws IOException, FileUploadException {
if (files.length > 0) {
filesSession.addAll(Arrays.asList(files));
// store the bytes somewhere
return "{\"success\": true}";
}
else {
return "{\"success\": false}";
}
}
#RequestMapping(value = "/create/upload", method = RequestMethod.POST, consumes="application/octet-stream", produces="application/json")
#ResponseBody()
public String handleImageUploadApplication(HttpServletRequest request,
#ModelAttribute(value="files") List<MultipartFile> filesSession) throws IOException, FileUploadException {
if (request.getInputStream() != null) {
// creamos el fichero temporal
File file = File.createTempFile("file", "valumns",
RepositoryData.getRepositoryData());
FileOutputStream fos = new FileOutputStream(file);
// copiamos contenido
Streams.copy(request.getInputStream(), fos, true);
//TODO:
//filesSession.addAll(Arrays.asList(files));
// store the bytes somewhere
return "{\"success\": true}";
}
else {
return "{\"success\": true}";
}
}
#ExceptionHandler(Exception.class)
#ResponseStatus(value = HttpStatus.SERVICE_UNAVAILABLE)
public void handleException(Exception ex) {
log.error("Ocurrio un error en el album", ex);
}
I had the same problem with the fineuploader (valums), and I tried using request.getInputStream() but did not get it to work.
The #ResponseBody annotation worked but I got the whole body with headers. I thought processing that and stripping off the unwanted chunks was not very elegant.
I looked further and found the solution is this post:
problem with spring ajax file upload
Like it is said, I added the bean configuration for the multipart resolver to my spring configuration
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
After that, I could easily retrieve my file using
public #ResponseBody Map ajaxUploadFile(#RequestParam MultipartFile qqfile) { ... }
Don't forget to add the Apache commons-io.jar and commons-fileupload.jar libraries in your project to get it to work
When using valums plugin I solved this problem by using #RequestBody Spring annotation.
You could rewrite your code as follows:
#RequestMapping(value ="/settingsSim",method=RequestMethod.POST)
#ResponseBody
public Map uploadSimSettings(#RequestBody String body) {
/*
some controller logic
*/
}
Note that the variable body will contain the contents of the uploaded file. Also there is no method declaration in your example which means that your method will be mapped to GET request.
P.S. I also had this "no multipart boundary" problem when parsing request with Apache Commons. HttpServletRequest#getParts() returns just an empty collection.
#Tomas I encountered same issue while using the same jquery plugin. Please change the Content-Type in the plugin code to xhr.setRequestHeader("Content-Type", "multipart/form-data"); on my plugin its line 1203, after this its now showing a stack trace, however I am encountering another issue where the logs are printing :
Sep 8, 2011 9:43:39 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet dispatcher threw exception
org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
As per my observation the file upload plugin does not send a multipart file but sends a stream. I could get it to work by declaring the controller method to accept filename as request param qqfile and the second parameter as httprequest. I then did further processing using request.getinputstream. Hope that helps!
Regards,
Pradyumna

Apache HttpClient making multipart POST to Spring #Controller class

It seems like there are several posts such as here asking how to use Apache Commons HTTPClient libraries in Java to do a POST to a Servlet. However, it seems like I'm having some problems doing the same thing with a annotated Spring controller method. I've tried a few things but gotten HTTP 401 Bad Request responses from the server. Any examples of doing this would be greatly appreciated.
EDIT: Code I am trying to use:
//Server Side (Java)
#RequestMapping(value = "/create", method = RequestMethod.POST)
public void createDocument(#RequestParam("userId") String userId,
#RequestParam("file") MultipartFile file, HttpServletResponse response) {
// Do some stuff
}
//Client Side (Groovy)
void processJob(InputStream stream, String remoteAddress) {
HttpClient httpclient = new DefaultHttpClient()
httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1)
HttpPost httppost = new HttpPost("http://someurl/rest/create")
MultipartEntity mpEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE)
InputStreamBody uploadFilePart = new InputStreamBody(stream, 'application/octet-stream', 'test.file')
mpEntity.addPart('file', uploadFilePart)
mpEntity.addPart('userId', new StringBody('testUser'))
httppost.setEntity(mpEntity)
HttpResponse response = httpclient.execute(httppost);
println(response.statusLine)
}
Still getting 400 Bad Request in the response from the server.
I hate to answer my own question when it shows incompetence, but it turns out the code was fine, this particular controller did not have a CommonsMultipartResolver defined in its servlet-context.xml file (multiple DispatcherServlets...long story :()
Here's what I added to make it work:
<!-- ========================= Resolver DEFINITIONS ========================= -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="50000000"/>
</bean>
Here is an example from the Spring Reference:
#Controller
public class FileUpoadController {
#RequestMapping(value = "/form", method = RequestMethod.POST)
public String handleFormUpload(#RequestParam("name") String name,
#RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
byte[] bytes = file.getBytes();
// store the bytes somewhere
return "redirect:uploadSuccess";
} else {
return "redirect:uploadFailure";
}
}
}

Resources