FileSaver.js with Angular 7 - excel

I have a use case where I need to download data as an excel file. I am using Angular 7 (UI) and Java Spring REST API service. The REST API will generate the Excel file and send it to Angular where I use FileSaver.js module to save the file to disk. I see the blob being created and the size printed on the console, but the file does not save to disk.
This used to work when I had Angular 4. Any help is greatly appreciated.
Also, if anybody can suggest an alternative node module to achieve the same result, I am all ears.
Below is the code snippet in Angular
this.myService.getDataAsXLSX(this.dataList).
subscribe(byteArray => {
this.excelFileAsByteArray = byteArray;
}, error => console.log("There was an error."),
() => {
const blob = new Blob([this.excelFileAsByteArray.body], {type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; ; charset=UTF-8'});
console.log('Blob size in filtered = ' + blob.size);
FileSaver.saveAs(blob, "MyData-" + this.datePipe.transform(this.currentDate, 'MM-dd-yyyy') + ".xlsx"); } );
Here is the code from the REST API
ByteArrayOutputStream outputStream = new ExcelWriter().createExcelWorkbook(dataList, fullName);
byte[] byteArray = outputStream.toByteArray();
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Content-Type","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; ; charset=UTF-8");
responseHeaders.set("Content-Disposition", "attachment;filename=MyData.xlsx"); responseHeaders.set("Expires", "0");
return new ResponseEntity(byteArray, responseHeaders, HttpStatus.OK);

Have you set responseType as 'blob' as 'json'. Content type as 'application/vnd.ms-excel'
public getDataAsXLSX(list: Array<any>) {
return this.http.get(this.url, {
headers: this.headers,
responseType: 'blob' as 'json'
});
}

Related

How to transfer image from server to client with node http header size restrictions

Transferring image (base64 encoded, created with Mapguide server) to client. I am able to output the image to the console and test it is correct. Using Node with npm and Vite for develpment web server. When I try to set imgLegend.src = data; I get this error "431 (Request Header Fields Too Large)" I believe it is the Node default max-http-header-size causing the problem. Have attempted to set --max-http-header-size=80000 with no luck. I am starting my dev server in package.json file like this: "start": "vite --host 0.0.0.0",
Does anyone know of a way around this or a better way to transfer the image from server to client?
here is the relevant code.
Client side:
//add legend
const mapVpHeight = document.getElementById('map').clientHeight;
var url = mgServer + "/Cid_Map/LayerManager.aspx/GetLegendImage";
var values = JSON.stringify({ sessionId: sessionId, mgMapName: mapName, mapVpHeight: mapVpHeight });
var imgLegend = new Image();
//console.log(values);
$.ajax({
url: url,
type: "POST",
contentType: "application/json; charset=utf-8",
data: values,
dataType: 'html',
success: function (data) {
console.log(data); //
imgLegend.src = data; //node.js won't allow http header as large as this image, about 18kb
},
error: function (xhr, textStatus, error) {
console.log(textStatus);
}
});
Server Side:
[WebMethod]
public static string GetLegendImage(string sessionId, string mgMapName, int mapVpHeight)
{
string tempDir = System.Configuration.ConfigurationManager.AppSettings["tempDir"];
string legFilePath = tempDir + sessionId + "Legend.png";
string configPath = #"C:\Program Files\OSGeo\MapGuide\Web\www\webconfig.ini";
MapGuideApi.MgInitializeWebTier(configPath);
MgUserInformation userInfo = new MgUserInformation(sessionId);
MgSiteConnection siteConnection = new MgSiteConnection();
siteConnection.Open(userInfo);
MgMap map = new MgMap(siteConnection);
MgResourceService resourceService = (MgResourceService)siteConnection.CreateService(MgServiceType.ResourceService);
map.Open(resourceService, mgMapName);
MgColor color = new MgColor(226, 226, 226);
MgRenderingService renderingService = (MgRenderingService)siteConnection.CreateService(MgServiceType.RenderingService);
MgByteReader byteReader = renderingService.RenderMapLegend(map, 200, mapVpHeight, color, "PNG");
MgByteSink byteSink = new MgByteSink(byteReader);
byteSink.ToFile(legFilePath);
//try this
//byte[] buffer = new byte[byteReader.GetLength()]; //something doesn't work here byteReader doesn't give comeplete image
//byteReader.Read(buffer, buffer.Length);
//loading image file just created, converting to base64 image gives correct image
string legendImageURL = "";
using (Stream fs = File.OpenRead(legFilePath))
{
BinaryReader br = new System.IO.BinaryReader(fs);
byte[] bytes = br.ReadBytes((int)fs.Length);
string strLegendImage = Convert.ToBase64String(bytes, 0, bytes.Length);
legendImageURL = "data:image/png;base64," + strLegendImage;
}
byteReader.Dispose();
byteSink.Dispose();
return legendImageURL;
//return buffer;
}
The 431 status code complains about the header length of your request ..
trace the request in your browsers dev tool network tab and study the header fields in your request in some special cases if your cookies get set to often with unique key value pairs this could be the problem...
May be you can copy and share the request response from your browsers network tab to provide some detailed information... especially the request response of the endpoint and look up the cookie/session storage maybe you find some suspicious stuff.
Good look :)

uploaded files to Azure are corrupted when using dio

I'm trying to upload a file from my phone to azure blob storage as a BlockBlob with a SAS. I can get the file to upload, but it can't be opened once downloaded. The file gets corrupted somehow. I thought this was a content-type problem, but I have tried several different approaches to changing to content-type. Nothing has worked so far.
My code:
FileInfo _fileInfo = await filePicker(); // get the file path and file name
// my getUploadInfo fires a call to my backend to get a SAS.
// I know for a fact that this works because my website uses this SAS to upload files perfectly fine
UploadInfo uploadInfo = await getUploadInfo(_fileInfo.fileName, _fileInfo.filePath);
final bytes = File(_fileInfo.filePath).readAsBytesSync();
try {
final response = await myDio.put(
uploadInfo.url,
data: bytes,
onSendProgress:
(int sent, int total) {
if (total != -1) {
print((sent / total * 100).toStringAsFixed(0) + "%");
}
},
options:
dioPrefix.Options(headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': mime(_fileInfo.filePath),
})
);
} catch (e) {
print(e);
}
This code uploads a file just fine. But I can't open the file since it becomes corrupted. At first, I thought this was a Content-Type problem, so I've tried changing the content type header to: application/octet-stream and multipart/form-data as well. That doesn't work.
I've also tried to do
dioPrefix.FormData formData =
new dioPrefix.FormData.fromMap({
'file': await MultipartFile.fromFile(
_fileInfo.filePath,
filename: _fileInfo.fileName,
)
});
...
final response = await myDio.put(
uploadInfo.url,
data: formData, // This approach is recommended on the dio documentation
onSendProgress:
...
but this also corrupts the file. It gets uploaded, but I can't open it.
I have been able to successfully upload a file with this code, but with this approach I cannot get any type of response so I have no idea whether it uploaded successfully or not (Also, I can't get the progress of the upload):
try {
final data = imageFile.readAsBytesSync();
final response = await http.put( // here, response is empty no matter what i try to print
url,
body: data,
headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': mime(filePath),
});
...
Any help would be greatly appreciated. Thanks
I tried to upload a file using dio in Dart to Azure Blob Storage, and then download and print the content of the file, as the code below.
import 'package:dio/dio.dart';
import 'dart:io';
main() async {
var accountName = '<account name>';
var containerName = '<container name>';
var blobName = '<blob name>';
var sasTokenContainerLevel = '<container level sas token copied from Azure Storage Explorer, such as `st=2019-12-31T07%3A17%3A31Z&se=2020-01-01T07%3A17%3A31Z&sp=racwdl&sv=2018-03-28&sr=c&sig=xxxxxxxxxxxxxxxxxxxxxxxxxx`';
var url = 'https://$accountName.blob.core.windows.net/$containerName/$blobName?$sasTokenContainerLevel';
var data = File(blobName).readAsBytesSync();
var dio = Dio();
try {
final response = await dio.put(
url,
data: data,
onSendProgress:
(int sent, int total) {
if (total != -1) {
print((sent / total * 100).toStringAsFixed(0) + "%");
}
},
options: Options(
headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': 'text/plain',
})
);
print(response.data);
} catch (e) {
print(e);
}
Response response = await dio.get(url);
print(response.data);
}
Then, I ran it and got the result as the figure below.
The content of the uploaded file as blob is the json string encoded from a Uint8List bytes from the funtion readAsBytesSync.
I researched the description and the source code of dio, actually I found dio is only suitable for sending the request body of json format, not for raw content as request body.
Fig 1. The default transformer apply for POST method
Fig 2. https://github.com/flutterchina/dio/blob/master/dio/lib/src/transformer.dart
So to fix it is to write a custom transformer class PutTransformerForRawData instead of the default one to override the function transformRequest, as the code below.
import 'dart:typed_data';
class PutTransformerForRawData extends DefaultTransformer {
#override
Future<String> transformRequest(RequestOptions options) async {
if(options.data is Uint8List) {
return new String.fromCharCodes(options.data);
} else if(options.data is String) {
return options.data;
}
}
}
And to replace the default transformer via the code below.
var dio = Dio();
dio.transformer = PutTransformerForRawData();
Then, you can get the data via the code below.
var data = File(blobName).readAsBytesSync();
Or
var data = File(blobName).readAsStringSync();
Note: the custom transfer PutTransformerForRawData is only for uploading, please remove the download & print code Response response = await dio.get(url); print(response.data);, the default transformer seems to check the response body whether be json format, I got the exception as below when my uploaded file is my sample code.
Unhandled exception:
DioError [DioErrorType.DEFAULT]: FormatException: Unexpected character (at character 1)
import 'dart:typed_data';

How to get excel file from disc in angular2 via rest

I am using an angular 2 with Java background and communication between them is through REST. What I have to do is to create some excel file on button click and then to return that file in the user API.
REST looks like this:
#RequestMapping(value = "/some_path/{someId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public FileSystemResource exportSomeData(#PathVariable long someId, HttpServletResponse response) {
// ... create excel file data...
File file = new File(fileName);
response.addHeader("FILE_NAME", fileName);
FileNameResource fsr = new FileNameResource(file);
return fsr;
}
In angular (return of the REST, call works ok):
getFile(path:String) {
this.autthHttp.get(`some_path')
.map((response) => {
let blob = (response)['body'];
return {
data: new Blob([blob], {type: 'application/octet-stream'}),
filename: response.headers.get('FILE_NAME')
}
})
.subscribe(res => saveAs(res.data, res.filename))
}
The problem is that I got the file, it contains data, but it lost it's metadata (show some question mark characters instead of format excel well in cells). Does somebody knows what can be the problem?
Try to set the responseType to Blob and use RC5 blob response type:
getFile(path:String) {
this.autthHttp.get(`some_path', {responseType: ResponseContentType.Blob})
.map((response) => {
let blob = response.blob();
return {
data: new Blob([blob], {type: 'application/octet-stream'}),
filename: response.headers.get('FILE_NAME')
}
})
.subscribe(res => saveAs(res.data, res.filename))
}

Download an excel file returned from a spring MVC Controller (Ajax call)

I'm developing a web application with spring boot, spring mvc.
I have a controller method that returned an excel file as the following:
in = new FileInputStream(comparsionreport);
out = response.getOutputStream();
response.setHeader("Content-disposition", "attachment; filename=" + comparsionreport.getName());
response.setContentType("application/vnd.ms-excel");
response.addHeader("filename", comparsionreport.getName());
byte[] buffer = new byte[BUFFER_SIZE]; // use bigger if you want
int length = 0;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
in.close();
out.close();
In view (javascript), an AJAX call is done like the following:
$.ajax({
url : $('#compareFrm').attr('action'),
type : 'post',
responseType: "arraybuffer",
data : $('#compareFrm').serialize(),
success : function(data, status, xhr) {
// download response file
var jsonData = JSON.stringify(data);
var blob = new Blob(
[ jsonData ],
{
type: "application/vnd.ms-excel"
});
saveAs(blob, xhr.getResponseHeader('filename'));
},
error : function(data) {
}
});
An excel file with the good name is downloaded but when open it, it says that file cannot be open because of wrong extension or format (corrupted file so...)
Anyone have any idea? it's a blocking issue for me :(
Thanks

Using HttpClient to upload files to ServiceStack server

I can't use the ServiceStack Client libraries and I've chosen to use the HttpClient PCL library instead. I can do all my Rest calls (and other json calls) without a problem, but I'm now stucked with uploading files.
A snippet of what I am trying to do:
var message = new HttpRequestMessage(restRequest.Method, restRequest.GetResourceUri(BaseUrl));
var content = new MultipartFormDataContent();
foreach (var file in files)
{
byte[] data;
bool success = CxFileStorage.TryReadBinaryFile(file, out data);
if (success)
{
var byteContent = new ByteArrayContent(data);
byteContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = System.IO.Path.GetFileName(file) ,
};
content.Add(byteContent);
}
}
message.Content = content;
Problem is now that I get a null reference exception (status 500) when posting. I doesn't get into the service. I see the call in the filterrequest, but that's it.
So I'm wondering what I do wrong and how I can pinpoint what is going wrong. How can I catch the correct error on the ServiceStack layer?

Resources