HTTP REST API File upload gives 400 in windows - java

I have a REST API for uploading files to server written in Java with Spring Boot. I am testing the API through postman. The API is working fine in Linux Environment, but in windows environment it returns 400 Bad Request as response. There is no change in code in both systems, as I am using the same jar file in both environments.
The POST request consist of a file attachment in the form-data and a parameter userName. My API accepts the data in that format only. I have checked the headers in the request in both the machines, and I am just passing a single header Content-Type as application/json.
Could someone guide me, what might be causing the error? I have checked some answers in stackoverflow for what might be the reasons for HTTP 400 other than the endpoint no being existing. Nothing answered my query.
Edit: Adding the code I am using to upload the file.
private static String UPLOADED_FOLDER_BIRTHDAY = "D:/uploads/birthday/";
#PostMapping("/api/upload/birthday")
public ResponseEntity<?> uploadFileForBirthday(#RequestParam("file") MultipartFile uploadfile, #RequestParam("userName") String userName) {
if (uploadfile.isEmpty()) {
return new ResponseEntity("please select a file!", HttpStatus.OK);
}
try {
saveUploadedFiles(Arrays.asList(uploadfile), UPLOADED_FOLDER_BIRTHDAY);
} catch (IOException e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity("Successfully uploaded - " +
uploadfile.getOriginalFilename(), new HttpHeaders(), HttpStatus.OK);
}
private void saveUploadedFiles(List<MultipartFile> files, String uploadPath) throws IOException {
for (MultipartFile file : files) {
if (file.isEmpty()) {
continue;
}
byte[] bytes = file.getBytes();
Path path = Paths.get(uploadPath + file.getOriginalFilename());
Files.write(path, bytes);
}
}

Related

How to return both JSON response and Byte Array of file in Springboot RestController Response while downloading a file

I am developing a rest controller to download a .docx file into the client system. My code is working fine as the file is getting downloaded. Now I want to enhance the response. My requirement is to also send a JSON payload in the response along with the .docx file content, something like
{"message":"Report downloaded Successfully"}
incase of successful download or with a different message incase of failure.
Below is my restcontroller code:
#RestController
public class DownloadController {
#PostMapping(value="/download",
consumes = {"multipart/form-data"},
produces = {"application/octet-stream"})
public ResponseEntity<?> downloadFile(#RequestParam("file") MultipartFile uploadFile){
//business logic to create the attachment file
try {
File file = new File("path_to_.DOCX file_I_have_created");
byte[] contents = Files.readAllBytes(Paths.get(file.getAbsolutePath()));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDisposition(ContentDisposition.attachment().filename("survey.docx").build());
return new ResponseEntity<>(contents, headers, HttpStatus.OK);
} catch (Exception e){
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
How do I modify my response code to send both the JSON message and the byte[] contents so that the file gets downloaded and I can see the JSON message in the response preview tab in the chrome or response body in postman?
UPDATE: I tried to define a response class like below
public class Downloadresponse {
private byte[] content;
private String message;
//getter,setters
}
With this change in place, I am getting below exception:
Resolved [ HttpMessageNotWritableException: No converter for [class ...Downloadresponse] with preset content-type "application/octet-stream”]
You can't. HTTP doesn't allow you to defined multiple content-types on 1 request/response. That being said, you could send the byte array base64 encoded as part of a json response but would need to handle it in the front-end (if you have any) as it would not trigger the file download process of the browser.
You can define custom class which hold your current content and message . So you can return that class in the response
ResponseClass
{
byte[] contents;
String message;
}
You can send a json object that contain the message and the file as encoded64 string.
And in the client side you decode it and download it.
public record MyRecord(String message, String encodedStringBase64, String filename) {}
...
try {
File file = new File("path_to_.DOCX file_I_have_created");
byte[] contents = Files.readAllBytes(Paths.get(file.getAbsolutePath()));
String encodedString = Base64.getEncoder().encodeToString(contents);
MyRecord record = new MyRecord("Report downloaded Successfully", encodedString, file.getName());
return log.traceExit(ResponseEntity.ok().headers(headers)
.contentType(MediaType.valueOf("application/json")).body(record));
} ...

Why does Chrome closes connection when REST API returns a large response?

I use this REST API
#GET
#Path("/get-file")
#Consumes(MediaType.APPLICATION_JSON + ";charset=UTF-8")
public void getFile(#Context HttpServletRequest request, #Context HttpServletResponse response,
#QueryParam("filePath") String filePath, #QueryParam("fileName") String fileName) throws IOException {fileName);
File fileToDownload = new File(filePath);
if (fileToDownload.exists()) {
java.nio.file.Path path = Paths.get(fileToDownload.toURI());
response.setContentType(MediaType.APPLICATION_OCTET_STREAM);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName);
try {
response.setHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(Files.size(path)));
Files.copy(path, response.getOutputStream());
response.flushBuffer();
} catch (IOException e) {
log.error(e.getLocalizedMessage(), e);
throw e;
}
} else {
throw new FileNotFoundException();
}
}
to download files generated from the server. It all works fine if the file i'm trying to get is less than 50GB (more or less), but when I try to download a 50GB while using Chrome, I get a java.net.SocketException: Connection reset exception. This doesn't happen in Firefox, and I can get the file as usual.
This is what I see in the Chrome Network Tab:
Downloading the file is the only operation I do and it the fails immediately.
If I omit the CONTENT_LENGTH header from the response, the size of the response reaches 48GB~ and then I get the same error.
Does anyone have any clue about this? On server side I use Java 7 (I tried with Java 8 too) and on client side I use AngularJS v1.5.11. I use ApacheTomcat to deploy my application. Let me know if you need more info about the problem.

Java spring file download proxy with rest calls

I need to create a rest service in java which will in turn connect to another rest service for file download. For now, I just need to transfer the file from the other backend to client but in future some processing/transformations would be done.
For all the web services in my project, we are using spring rest (for providing as well as consuming the services).
My question is what would be the appropriate way of doing it considering that the files would be large and I don't want to run into OutOfMemory errors.
People in some other posts have suggested to use streams on both the ends but is that really possible? For this, do I need to write the file on disk first?
My current code for file download (consumer) -
public BackendResponse<byte[]> callBackendForFile(BackendRequest request) {
String body = null;
ResponseEntity<byte[]> responseEntity = null;
URI uri = createURI(request);
MultiValueMap<String, String> requestHeaders = getHeadersInfo(request.getHttpRequest());
if (HttpMethod.GET.equals(request.getMethod())) {
responseEntity = restTemplate.exchange(uri, request.getMethod(),
new HttpEntity<String>(body, requestHeaders), byte[].class);
} else {
LOG.error("Method:{} not supported yet", request.getMethod());
}
BackendResponse<byte[]> response = new BackendResponse<>();
response.setResponse(responseEntity);
return response;
}
My client code (provider):
#RequestMapping(value = "/file", method = RequestMethod.GET, produces = "application/xml")
#ResponseBody
public void downloadFileWithoutSpring(HttpMethod method, HttpServletRequest httpRequest,
HttpServletResponse httpResponse) {
BackendRequest request = new BackendRequest(method,
httpRequest.getRequestURI(), httpRequest.getQueryString(), httpRequest);
BackendResponse<byte[]> backendResponse = dutyplanService.getFile(request);
ResponseEntity<byte[]> response = backendResponse.getResponse();
httpResponse.addHeader("Content-Disposition", "attachment; filename=\"" + "attachment.zip" + "\"");
httpResponse.getOutputStream().write(response.getBody());
httpResponse.flushBuffer();
}
Note: The code above doesn't work somehow as the attachment downloaded is a corrupt file
I don't think you will need to create that file on server as long as you are having the bytearray content of it received from another server.
You can try changing value of produces annotation to the value application/zip (or application/octet-stream, depending on the target browser) instead of 'application/xml'
you can pass HttpServletResponse#getOutputStream() directly in restTemplate and write it without save file in server.
public void getFile(HttpServletResponse response) throws IOException {
restTemplate.execute(
"http://ip:port/temp.csv",
HttpMethod.GET,
null,
clientHttpResponse -> {
StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());
return null;
}
);
}
note that after call getFile(), you should close outputStream like this
response.getOutputStream().close()

How to prevent Jackson from serializing the response from a controller method in Spring MVC based REST API?

I am developing an Angular 2 app that requires to serve a file by making a request for a file at the backend.
I am returning the requested file's File object from the backend. But the Jackson is serializing that response automatically, and I am getting file name as a response rather than a file as a whole.
Moreover the file is an XML file. Could anyone help as to how to prevent Jackson from serializing the Java File object that my controller method is sending?
EDIT
Added the controller code that does file serving
#RequestMapping(value = FatcaConstants.GET_XML_FILE, method = RequestMethod.GET)
public File getXML(#RequestParam(value = "filePath") String filePath) {
LOG.info("Getting the XML report from the file path in the Controller");
try {
File file = new File(filePath);
return file;
} catch (Exception e) {
LOG.info("Error in retrieving file " + filePath + " from the FileController");
LOG.info(e.getMessage());
return null;
}
}

Send file from Server to Client java with Spring and Rest web service

I have to send file from Server (from its file system) to Cliente (another pc and store file in a particularly folder) through java code and Rest web service. These files can be even 120MB.
I used MultipartFile to upload file from my web page but I don't know how to download from the client.
It would be good the possibility to use a REST web service that returns both file and message with result of method (true or false if there was an error).
Do you have an idea?
At the moment I use this code in server:
and the best way would be
#Override
#RequestMapping(value = "/oldmethod", method = RequestMethod.GET)
public #ResponseBody Response getAcquisition(#RequestParam(value="path", defaultValue="/home") String path){
File file;
try {
file = matlabClientServices.getFile(path);
if (file.exists()){
InputStream inputStream = new FileInputStream(path);
byte[]out=org.apache.commons.io.IOUtils.toByteArray(inputStream);
return new Response(true, true, out, null);
}
else
return new Response(false, false, "File doesn't exist!", null);
} catch (Exception e) {
ErrorResponse errorResponse= ErrorResponseBuilder.buildErrorResponse(e);
LOG.error("Threw exception in MatlabClientControllerImpl::getAcquisition :" + errorResponse.getStacktrace());
return new Response(false, false, "Error during file retrieving!", errorResponse);
}
}
but to the client the code below doesn't work:
public Response getFileTest(#RequestParam(value="path", defaultValue="/home") String path){
RestTemplate restTemplate = new RestTemplate();
Response response = restTemplate.getForObject("http://localhost:8086/ATS/client/file/oldmethod/?path={path}", Response.class, path);
if (response.isStatus() && response.isSuccess()){
try {
Files.write(Paths.get("PROVIAMOCI.txt"),org.apache.commons.io.IOUtils.toByteArray(response.getResult().toString()));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return response;
}
it writes the byte[] characters and not the original text
As per my understanding FileSystemResource used for get the file system using file system URI but it is not over the HTTP. Using FileSystemResource you can access files from your local machine to local accessible file system and from server to your server accesiable file system. But could not access from local to server file system.

Categories