My task is to download file on button click. Data in file should be fetched from Service. I did this approach in three steps:
1) Ajax call to first controller. In this controoler: create file, call service, get data from service, write to file AND return fileName to AJAX success() callback.
2) Using a response from first request (file name), I'm doing this:
window.location.href = 'downloadFile/' + response;
to call second controller that will download the file.
This approach perfectly works on my local computer.
My problem is, it makes two requests and each time the request will be made to different hosts (in prod). So it will not find the file in second controller.
So I cannot call controller two times. When I try to combine these two methods together in one controller method - it's not starting download a file.
Why it happens? Why it's starting download only if I return file name to success() callback and then call second controller method again that will write the file to HtttpServletResponse.?
$.ajax({
type : "GET",
contentType : "application/json",
url : "createFile",
dataType : 'text',
success : function(response) {
console.log(response);
window.location.href = 'downloadFile/' + response;
}
}); <%--end of AJAX call--%>
#RequestMapping(value = "/createFile", method = RequestMethod.GET)
#ResponseStatus(value=HttpStatus.OK)
#ResponseBody String createFile(#RequestParam("startDate") String startDate,
#RequestParam("endDate") String endDate,
#RequestParam("vendorId") String vendorId, HttpServletResponse httpResponse) throws Exception {
File file = new File("mynewfile.csv");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
List<Data> dataFromService = getDataFromService();
httpResponse.setHeader("Content-Disposition", "attachment; filename=\"" + "mynewfile" + ".csv\"");
httpResponse.setContentType("application/csv");
for (int i = 0; i < dataFromService.size(); i++) {
Data data = dataFromService.get(i);
writer.write(data.getVendorName() + ", "
+ data.getAsin() + ", "
+ data.getReferenceId() + ", "
+ data.getCompleteCount() + "\n");
}
writer.close();
return file.GetName();
}
#RequestMapping(value = "/downloadFile/{file}", method = RequestMethod.GET)
public void downloadFile(File metricsReportFile, HttpServletResponse response) throws Exception {
File metricsReportFile = new File (file);
InputStream is = new FileInputStream(metricsReportFile);
response.setHeader("Content-Disposition", "attachment; filename=" + metricsReportFile.getName() );
FileCopyUtils.copy(is, response.getOutputStream());
response.flushBuffer();
metricsReportFile.delete();
}
You said that in production doesn't work. Is there a Cluster of servers or something? Maybe the request of the file creation is attended by an instance and when you try to get the file, another one is responding
Related
I am building a REST API in Spring Boot for uploading and fetching file from the server, I want to upload various types of file that can either be text,image,audio,video,etc..
While uploading there is no problem, but when I want to display the file on my web page, on content is appearing, but I am getting the data from the server as a raw data.
I want to put that data into URL.createObjectURL() and then redirect to the URL which is generated.
There are some screenshots which I am uploading.
This is the data when I do console.log(response);
The code which I am using for AJAX
var form = new FormData();
form.append("qualifiedFilePath", "E://files/test.png");
var settings = {
"async": true,
"crossDomain": true,
"url": "http://localhost:8081/callTransaction/file",
"method": "POST",
"timeout": 0,
"processData": false,
"mimeType": "multipart/form-data",
"contentType": false,
"Accept": "image/png",
"data": form
};
$.ajax(settings).done(function(response) {
console.log(response);
const objectURL = URL.createObjectURL(new Blob([response], {
"type": "image/png"
}));
console.log(objectURL);
});
I get the URL:
blob:http://localhost:8080/81c9fbde-5e84-400e-8d92-5da6fc02c7ef
Output:
The Source Code in Spring Boot:
Controller:
#PostMapping(path="/file")
#CrossOrigin(origins = "http://localhost:8080")
public ResponseEntity<Resource> loadFile(#RequestPart("qualifiedFilePath") String qualifiedFilePath, HttpServletRequest request)
{
return ctbl.loadFile(qualifiedFilePath,request);
}
BusinessLogic:
public ResponseEntity<Resource> loadFile(String qualifiedFilePath, HttpServletRequest request)
{
Resource file=null;
if(qualifiedFilePath==null)
{
return new ResponseEntity<Resource>(file,HttpStatus.BAD_REQUEST);
}
try {
file=ctdi.loadFile(qualifiedFilePath);
} catch (MalformedURLException e) {
return new ResponseEntity<Resource>(file,HttpStatus.INTERNAL_SERVER_ERROR);
}
if(file==null)
{
return new ResponseEntity<Resource>(file,HttpStatus.NO_CONTENT);
}
String contentType = null;
try {
contentType = request.getServletContext().getMimeType(file.getFile().getAbsolutePath());
} catch (IOException ex) {
return new ResponseEntity<Resource>(file,HttpStatus.INTERNAL_SERVER_ERROR);
}
if(contentType == null) {
contentType = "application/octet-stream";
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
.body(file);
}
DAO:
#Override
public Resource loadFile(String qualifiedFilePath) throws MalformedURLException {
Path filePath = Paths.get(qualifiedFilePath);
Resource resource = new UrlResource(filePath.toUri());
return resource;
}
It has been a long time to answer since the question was posted, but the solution to it has been discovered.
It has a very simple solution.
I used the program logic from the CalliCoder webiste[the link is attached below], by using this I was able to store files, view files and download them too, they have given a very nice explanation of the program and how to do it.
By using this we can make a URL(endpoint) by which we can access the file we are approaching for, they too have given an example for accessing the file.
They have made a web-based front-end in which they are trying to upload files, but the module for displaying the file/downloading the file in front-end is missing.
Yes, We can just copy the URL(endpoint) in the browser and it starts displaying/playing the file.
If we want to use URL.createObjectURL() for making a temporary and local URL for the same source, then we can do like this:
URL.createObjectURL(await fetch("URL(endpoint)").then(r => r.blob()));
This example is taken from Stackoverflow[The link is attached bellow]
References:
CalliCoder
StackOverflow
I have developed a code which takes input Json and Multipart file as input. But, when I am trying to call this API in postman, I am getting error as:
Required request part 'input' is not present"
Here is my controller:
#RequestMapping(value={"/saveThumbnail"}, method={RequestMethod.POST},
consumes={"multipart/form-data"}, headers={"Accept=application/json"})
public ImageConversionOutput convertImageIntoThumbnail(#RequestPart ImageConversionInput input, #RequestPart(value = "file") MultipartFile file)
{
ImageConversionService service = new ImageConversionService();
System.out.println(input);
ImageConversionOutput output = null;
try {
output = service.convertImageToThumbnail(input, file, imageRepository);
}
catch(Exception e)
{
e.printStackTrace();
}
return output;
}
My postman Request is as follows:
I am getting below error:
Is your input param in json format?
Try {"fileName": "test7", "fileType": "png", "barCodeFlag":"F"}
#RequestPart
while RequestPart is likely to be used with parts containing more complex content e.g. JSON, XML).
Request :
//file: request.js
var form = new FormData();
// user is a json string, not an object.
form.append('user', JSON.stringify({name: 'John', age: 18}));
// append the file
form.append('file', file));
Controller :
#PostMapping("/saveUser")
public User saveUser(
#RequestPart(value = "user") UserProfile user,
#RequestPart(value = "file") MultipartFile file)
{
// code..
}
I tried everything, and I am constantly getting this error from Spring Boot
Resolved exception caused by Handler execution: org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
This is my Angular code. NOTE: I am using Http (not HttpClient) for my POST request.
updateUserProfilePicViaHttp(userId: number, imageFile: any) {
let headers: Headers = new Headers()
headers.append('Content-Type', 'multipart/form-data;boundary=imageUpload');
let formData: FormData = new FormData()
formData.append('file', imageFile, imageFile.name)
return this.http.post(this.baseUrl + `user/${userId}/profile_pic`, formData, { headers: headers })
}
This is my Spring Boot code
#RequestMapping(value="/{userId}/profile_pic",
method = RequestMethod.POST,
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#ResponseBody
public ResponseEntity<User> uploadProfilePic(#PathVariable("userId") Integer id, #RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes){
IUserDao userDao = (IUserDao) getDao();
User user = null;
try {
user = userDao.saveAndUpdateProfilePic(id, file.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
if( user != null)
return new ResponseEntity<>(user, HttpStatus.OK);
else
return new ResponseEntity<>(user, HttpStatus.NOT_FOUND);
}
I think that there is something wrong with my Angular code, because when I send an image via Postman everything works fine!
Postman image:
Thanks!
EDIT: For some reason I tried replacing #RequestParam("file") MultipartFile file with #RequestParam MultipartFile file in my function.
Still getting the same error
It seems that when Spring says
Required request part 'file' is not present
It associates name 'file' with reference name of MultiPartFile file in my Spring boot function, not with #RequestParam('file').
EDIT2: I listened to Ravat and modified my code a little bit.
How I got imageFile?
Explanation:
#ViewChild('fileInput') myFile: ElementRef reference from <input type="file" #fileInput>
This is what is imageFile in my Angular function.
imageFile = this.myFile.nativeElement.files[0]
But still, same error...
After 6 hours of googling and explaining to Duck Overflow what is the problem, I stumbled upon this.
And I just removed my header
headers.append('Content-Type', 'multipart/form-data;boundary=imageUpload');
Final code
updateUserProfilePicViaHttp(userId: number, imageFile: any) {
let formData: FormData = new FormData()
formData.append('file', imageFile, imageFile.name)
return this.http.post(this.baseUrl + `user/${userId}/profile_pic`, formData)
}
The Issue is in following code code
updateUserProfilePicViaHttp(userId: number, image: string) {...}
I don't know about angular, here you are sending string in an image variable
that definitely won't work because it needs to be a file.
I am trying to get my file upload functionality done using Angular2 and SpringBoot. I can certify that my java code for the file uploading working fine since I have tested it successfully using Postman.
However, when it comes to sending the file from Angular2 front end, I am getting the HTTP 400 response saying Required request part 'file' is not present.
This is how I send the POST request from Angular2.
savePhoto(photoToSave: File) {
let formData: FormData = new FormData();
formData.append('file', photoToSave);
// this will be used to add headers to the requests conditionally later using Custom Request Options
this._globals.setRequestFrom("save-photo");
let savedPath = this._http
.post(this._endpointUrl + "save-photo", formData)
.map(
res => {
return res.json();
}
)
.catch(handleError);
return savedPath;
}
Note that I have written a CustomRequestOptions class which extends BaseRequestOptions in order to append Authorization header and Content Type header. Content Type header will be added conditionally.
Following is the code for that.
#Injectable()
export class CustomRequestOptions extends BaseRequestOptions {
constructor(private _globals: Globals) {
super();
this.headers.set('X-Requested-By', 'Angular 2');
this.headers.append('virglk', "vigamage");
}
merge(options?: RequestOptionsArgs): RequestOptions {
var newOptions = super.merge(options);
let hdr = this._globals.getAuthorization();
newOptions.headers.set("Authorization", hdr);
if(this._globals.getRequestFrom() != "save-photo"){
newOptions.headers.set('Content-Type', 'application/json');
}else{
//request coming from save photo
console.log("request coming from save photo");
}
return newOptions;
}
}
This conditional header appending is working fine. The purpose of doing that is if I add 'Content-Type', 'application/json' header to every request, file upload method in Spring controller will not accept it. (Returns http 415)
Everything seems to be fine. But I get Required request part 'file' is not present error response. Why is that? I am adding that param to the form Data.
let formData: FormData = new FormData();
formData.append('file', photoToSave);
This is the Spring Controller method for your reference.
#RequestMapping(method = RequestMethod.POST, value = "/tender/save-new/save-photo", consumes = {"multipart/form-data"})
public ResponseEntity<?> uploadPhoto(#RequestParam("file") MultipartFile file){
if (file.isEmpty()) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage("DEBUG: Attached file is empty");
return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND);
}
String returnPath = null;
try {
// upload stuff
} catch (IOException e) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage(e.getMessage());
return new ResponseEntity<ErrorResponse> (errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<String>(returnPath, HttpStatus.OK);
}
EDIT - Adding the payload of the request captured by the browser
As you can see, the param "file" is available there.
Try to add
headers: {
'Content-Type': 'multipart/form-data'
},
to your
.post(this._endpointUrl + "save-photo", formData)
Change formData.append('file', photoToSave);
to formData.append('file', this.photoToSave, this.photoToSave.name); and also add headers specifying the type of data you are passing to API, in your case it will be 'Content-Type': 'multipart/form-data'. Post the output here if it fails even after changing this.
Is there a chance that you're using zuul in a secondary app that is forwarding the request? I saw this with an update where the headers were stripped while forwarding a multi-part upload. I have a gatekeeper app which forwards requests using zuul to the actual service via a looking from eureka. I fixed it by modifying the url like this:
http://myserver.com/service/upload
to
http://myserver.com/zuul/service/upload
Suddenly the 'file' part of the upload header was no longer stripped away and discarded.
The cause, I suspect was a re-try mechanism which cached requests. On failure, it would re-submit the requests, but somehow for file uploads, it wasn't working properly.
To upload a file to the server, send your file inside a FormData and set content type as multipart/form-data.
export const uploadFile = (url, file, onUploadProgress) => {
let formData = new FormData();
formData.append("file", file);
return axios.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data;charset=UTF-8',
// other headers
},
onUploadProgress,
})
};
To handle file object, be careful with consumes attribute and #RequestPart annotation here.
#PostMapping(value = "/your-upload-path", consumes = "multipart/form-data")
public ResponseEntity<Object> uploadFile(#RequestPart("file") #Valid #NotNull #NotBlank MultipartFile file) {
// .. your service call or logic here
}
I am having a real tough time with this. I have done a ton a research and nothing has worked. Please help. I have a spring REST call and a jquery ajax PUT and POST method that both give a 400 bad request. I have done JSON.stringify, raw object data, everything. I can't seem to find what is wrong and the ONLY clue I have is one 400 bad request error with no stack trace, nothing about how it SHOULD be formatted...nothing. Is there a way I can get more information as to what is wrong? It does not even get to the first line in the spring REST method below.
Spring REST code:
#RequestMapping(value = "/supervisor/agent", method=RequestMethod.PUT)
public void updateAgent(#RequestBody AgentDTO agent)
{
try {
AgentDTO sessionAgent = (AgentDTO) session.getAttribute(ADAuthenticationSuccessHandler.SESSION_AGENT);
RestTemplate restTemplate = new RestTemplate();
log.debug("Supervisor updating agent:"+agent);
String allURL = acrURL+"/company/"+sessionAgent.getCompanyGuid()+"/supervisor/"+sessionAgent.getAgentGuid()+"/agent/"+agent.getAgentGuid();
log.debug("Supervisor updating agent url:"+allURL);
agent.setEnabled(Mytime.ENABLED);
restTemplate.put(allURL, agent);
log.debug("Supervisor Agent updated");
} catch (Exception e) {
log.error("Error supervisor updating agent");
e.printStackTrace();
}
}
Here is the JQuery ajax call:
function editAgent()
{
console.log("edit agent");
console.log("chosenAgent:"+chosenAgent);
var anAgent = myAgents[chosenAgent];
anAgent.firstName = $('#SDEAFirstName').val();
anAgent.lastName = $('#SDEALastName').val();
anAgent.addressEmail = $('#SDEAEmail').val();
console.log(anAgent);
console.log(anAgent.agentGuid);
// var testData = '{"addressEmail": "agent7#csi.com","agentGuid": "EC165F8A-28F4-4765-BDC5-893722FCF6AA","firstName": "Agent","lastName": "0071","workStatus": "Offline}';
$.ajax({
type : 'PUT',
url : "/mytime.agentdesktop/supervisor/agent",
contentType : "application/json; charset=utf-8",
data: JSON.stringify(anAgent),
dataType : 'json',
success : function(result)
{
console.log("Agent edited:",anAgent.agentGuid);
init = false; // needed to reload agent data. Otherwise, it just grabs it but doesn't update gui
getMyAgents(); // reload agent data now that this agent was deleted
},
error : function(jqXHR, textStatus, errorThrown)
{
console.error("editAgent:status:"+textStatus+" error:", errorThrown);
}
});
editAgentDialog.dialog('close');
}
#RequestMapping(value = "/supervisor/agent", method=RequestMethod.PUT, consumes = "application/json")
You need to state which type of data you would be receiving in controller.
consumes = "application/json" does this.