Unable to download Excel spreadsheet with outputstream data from backend - angularjs

The REST method to return outputStream data to download an Excel spreadsheet:
#RequestMapping(value = "/downloadxls", method = RequestMethod.GET)
public #ResponseBody void getRecordsAndExportExcel(#RequestParam("search_param") String students, HttpServletResponse response) {
response.setContentType("application/vnd.ms-excel");
Workbook hssfWorkbook = exportExcelService.getExcelStudents(students);
try {
OutputStream out = response.getOutputStream();
hssfWorkbook.write(out);
out.flush();
out.close();
} catch (IOException e) {
logger.error("Error exporting to excel:" + e);
}
}
I am getting the data as bytes, but in Angular I am trying to present it as an Excel spreadsheet; but it won't download. I am doing this for the conversion:
var blob = new Blob([result.data], {type : 'application/vnd.openxmlformats-officedocument.presentationml.presentation;charset=UTF-8'});
FileSaver.saveAs(blob, "MyData.xls");
The request and response headers:
Access-Control-Allow-Headers:x-requested-with, content-type
Access-Control-Allow-Methods:GET OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:3600
Content-Type:application/vnd.ms-excel;charset=UTF-8
Server:Apache-Coyote/1.1
Transfer-Encoding:chunked
X-Application-Context:application:8080
Request Headers
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8,ms;q=0.6
Connection:keep-alive
Host:localhost:8080
Origin:http://localhost:9000
Referer:http://localhost:9000/
I am making a GET-call from frontend using Angular and calling backend to download the data as an Excel spreadsheet, but it is not able to convert the output stream to blob/excel. How can I present an Excel spreadsheet as download?

The way I would present an excel sheet in my application is to return the path+filename of the excel sheet to show and then open the link using angular in a new tab.
In the service class
ExportExcel response = new ExportExcel();
response.setPath(excelPath);
return response;
Controller class
Response response = excelService.generateExcel();
return new ResponseEntity<>(response, HttpStatus.OK);
In your angular controller:
if(response.status === 200){
var excelPath = response.data.path;
var win = window.open(excelPath, '_blank');
if (win) {
//Browser has allowed it to be opened
win.focus();
} else {
//Browser has blocked it
swal(
"Error!",
'Please allow popups for this website',
"error"
);
}
}
Try and see if this works.

Related

Azure Function HTTP Multi-Part POST Not Recognizing Field

I am trying to post to a web service using a multi-part form. However the service is saying it can't recognize the file upload I am trying to send. This is the section for the file upload.
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="GOO1242.pdf"
Content-Type: application/pdf
JVBERi0xLjQKJdP0zOE....... <rest of file>
----WebKitFormBoundary7MA4YWxkTrZu0gW
The error is
{
"type": "validation_error",
"detail": "There is no field: "file"."
}
Any ideas what is wrong?
Not sure if this is what you're looking for but I've used RestSharp to do this in a C# azure function
var apiRequest = await GetRestRequest(request);
var response = await SendRequest(apiRequest, log);
private async Task<RestRequest> GetRestRequest(string requestInput)
{
var uploadRequestInput = JsonConvert.DeserializeObject<UploadModelRequest>(requestInput);
var base64EncodedBytes = Convert.FromBase64String(uploadRequestInput.Content);
var uploadBicxRequest = JsonConvert.DeserializeObject<UploadModelRequest>(requestBody);
var apiRequest = new RestRequest("system/archive/document", Method.POST);
apiRequest.AddParameter("key", JsonConvert.SerializeObject(uploadRequest), "application/json", ParameterType.RequestBody);
apiRequest.AddFileBytes("VersionData", base64EncodedBytes, uploadRequest.PathOnClient, "application/octet-stream");
apiRequest.AddHeader("Content-Type", "multipart/form-data");
return apiRequest;
}

Sending FormData to Spring boot rest API get bad request 400

I built my API using spring boot.
#RequestMapping(value = "/v1/users/profile-picture/update", method = RequestMethod.POST)
public Object updateProfilePicture(Principal principal, #ModelAttribute UpdateProfilePictureDTO profile_picture){
Long user_id = accessTokenHandler.getIdByPrincipal(principal);
if(user_id == null)
return new DefaultResponseDTO(201,ResponseStatus.INVALID_USER,"No such user.");
if(profile_picture.getProfile_picture() == null)
return new DefaultResponseDTO(201,ResponseStatus.MISSING_INPUTS,"Profile Picture is missing.");
return userService.updateProfilePicture(user_id, profile_picture.getProfile_picture());
}
I want to send an image file to this controller. I tried with react.js. First I build formData and append the image into form data.
let formData = new FormData()
formData.append(
'profile_picture',
newFileList[0],
)
React API end point,
export async function profilePictureUpdate(formData) {
//image must be send as formdata
const response = await http.post(
apiEndPoint + '/profile-picture/update',
formData,
{
headers: {
Authorization: `Bearer ${getJwt()}`,
"Content-type": "multipart/form-data",
}
});
console.log("response of profile picuter", response);
return response
}
But when submit an image file, get a 400 bad request. How can I solve this?
Spring Boot (2.4.x): replace #ModelAttribute UpdateProfilePictureDTO profile_picture with #RequestParam(value = "profile_picture") MultipartFile file and then process the file. Make sure MultipartAutoConfiguration enabled (docs). If you registered a servlet via ServletRegistrationBean add multipart config to it:
servletRegistrationBean.setMultipartConfig(new MultipartConfigElement("/tmp",
multipartProperties.getMaxFileSize(),
multipartProperties.getMaxRequestSize(),
multipartProperties.getFileSizeThreshold()));

Throwing error while downloading file in ASP.NET Core with status code 400

I'm working on download feature. When I run code on localhost, it runs perfectly without any error. But when I uploads same code to server then it returns Failed to load resource: the server responded with a status of 400 (Bad Request) error in console of browser. For requesting and getting response from server, I have used Axios. I'm newbie to ASP.NET Core + ReactJS technology stack and working on APIs for the first time so it's being difficult for me to figuring out root cause of this error.
Here is my code for requesting data from server.
axios({
method: 'POST',
url: '/api/ImageFetch/ImageFetchPoint',
responseType: 'blob',// important
headers: {
'Content-Type': 'application/json'
},
data: filepath
}).then(function(response) {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
}).catch(error => {
console.log("Status:", error.response.status);
console.log("Data:", error.response.data);
});
For getting response as file(any extension) from server, I've used following code.
[HttpPost("PanImageFunction")]
[Route("api/[controller]/[action]")]
public async Task<IActionResult> ImageFetchFunction([FromBody] ImageFetchRequest request)
{
var filePath = request.ImagePath;
var filename = System.IO.Path.GetFileName(filePath);
string path="unknownpath";
try {
if (filename == null)
return Content("filename not present");
path = Path.Combine(
Directory.GetCurrentDirectory(),
"RegistrationImages", filename);
} catch(Exception ex) {
new Logger().write(ex);
}
var memory = new MemoryStream();
using (var stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = false // false = prompt the user for downloading; true = browser to try to show the file inline
};
Response.Headers.Add("Content-Type", "multipart/form-data");
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("X-Content-Type-Options", "nosniff");
memory.Position = 0;
return File(memory, GetContentType(path), Path.GetFileName(path));
}
private string GetContentType(string path)
{
var types = GetMimeTypes();
var ext = Path.GetExtension(path).ToLowerInvariant();
return types[ext];
}
private Dictionary<string, string> GetMimeTypes()
{
return new Dictionary<string, string>
{
{".txt", "text/plain"},
{".pdf", "application/pdf"},
{".doc", "application/vnd.ms-word"},
{".docx", "application/vnd.ms-word"},
{".xls", "application/vnd.ms-excel"},
{".png", "image/png"},
{".jpg", "image/jpeg"},
{".jpeg", "image/jpeg"},
{".gif", "image/gif"},
{".csv", "text/csv"}
};
}
Complete code works perfectly on localhost. But when I uploads it on server then it throws 400 bad request error on console of browser as you can see in following image.
I have checked many forums and articles but most of the articles related to ASP.NET core giving solution related to Razor pages and not giving solutions related to ReactJS and Axios.
How can I fix this error?

Unable to Download xlsx on client side using Filesaver.JS in React

I tried the previous solutions on stackoverflow as well as other options including defining responseType as "arraybuffer" and "blob", I'm not able to download xlsx file generated on the client side. The repsonse.data is in the form of byte[].
I see that the file has contents, but when I try to open it, it says - "The file format or file extension is not valid "
UI Snippet:
axios.post('/api/report/generateReport', {}, {
params: {
reportId: this.state.reportId,
lob: this.state.lob
}
}, {responseType: 'arraybuffer'})
.then(function (response) {
var blob = new Blob([response.data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
FileSaver.saveAs(blob, 'Metrics.xlsx');
})
Backend Snippet
#POST
#Path("/generateReport")
public Response generateReport(#QueryParam("reportId") int reportId, #QueryParam("lob") String lob) {
Response response = null;
byte[] contents = null;
contents = metricsReport.generateReport(lob);
response = Response.status(Response.Status.OK).entity(contents).header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.header("Content-Disposition", "attachment; filename=Metrics.xlsx").allow("OPTIONS")
.build();
return response;
}
I think you need to set content-type to application/octet-stream in the response from the server.
Something like(spring boot)-
return ResponseEntity.ok().contentLength(file.contentLength())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header("Content-disposition", "attachment; filename=Metrics.xlsx")
.body(inputStreamResource)

Angular: can't download server side generated spreadsheet

I'm trying to download a server generated spreadsheet.
My application uses Angular on the front-end and Java on the back-end.
Here's the method on the back-end that receives the request to generate and return the file:
#RequestMapping(value = "download", method = RequestMethod.GET, produces = "application/xls")
public ResponseEntity<InputStreamResource> download() throws IOException {
ByteArrayOutputStream fileOut = new ByteArrayOutputStream();
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet worksheet = workbook.createSheet("POI Worksheet");
HSSFRow row1 = worksheet.createRow((short) 0);
HSSFCell cellA1 = row1.createCell((short) 0);
cellA1.setCellValue("Hello!");
HSSFCellStyle cellStyle = workbook.createCellStyle();
cellStyle.setFillForegroundColor(HSSFColor.GOLD.index);
cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
cellA1.setCellStyle(cellStyle);
workbook.write(fileOut);
fileOut.close();
byte[] file = fileOut.toByteArray();
return ResponseEntity
.ok()
.contentLength(file.length)
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(new ByteArrayInputStream(file)));
}
And on the front-end, the following function is executed when the user clicks on Download button:
$scope.exportFile = function() {
$http.get('http://127.0.0.1:8000/api/excel/download')
.success(function (data, status, headers, config) {
var anchor = angular.element('<a/>');
anchor.attr({
href: 'data:application/octet-stream;charset=utf-8,' + encodeURI(data),
target: '_blank',
download: 'spreadsheet.xls'
})[0].click();
})
.error(function (data, status, headers, config) {
// handle error
});
};
The returned spreadsheet contains unreadable characters.
If I access http://127.0.0.1:8000/api/excel/download directly, the spreadsheet is downloaded without the .xls extension (with no extension at all). If I rename the file adding the .xls extension and then open it, I can see the file contents. So I think the problem it's on the call Angular does to back-end and not on generating the file on Java.
Has anyone experienced this situation or have some example to share? What am I doing wrong?
The Content-Disposition header should do the trick. So, you would have something like this:
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("Attachment", "spreadsheet.xls");
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentLength(file.length);
return new ResponseEntity<InputStreamResource>(
new InputStreamResource(new ByteArrayInputStream(file)),
headers, HttpStatus.OK);

Resources