I am working on a task, in which I have to download a report in xlsx format. The report file is generated successfully from server, and is received on client side as well. But it is not opening and producing invalid format error.Below is the code of server side.
var output = await reportObj.GetExcelData(rParams);
if (output != null){
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(output.ConentBytes)
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = output.FileName
};
return result;
}
Here is the code for client side:
var saveData = function (response) {
if (response.status === 200) {
var reportData = response.data;
var b = new Blob([reportData], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
saveAs(b,"ReportFile.xlsx");//this is FileSaver.js function
} else {
console.log(response.statusText);
}
};
$scope.getExcelFile = function(reportName, reportParams) {
reportDataService.getExcelReportData(reportName, reportParams, saveData);
}
Below is the error message:
Excel could not open newFile.xlsx because some content is unreadable. Do you want to open and repair this workbook?
On clicking repair, following error appears:
Excel cannot open this file.
The file format or file extension is not valid. Verify that the file has not been corrupted and that the file extension matches the format of the file.
Can some one guide me what I am doing wrong? Meanwhile, the same server side file generator objects works smoothly in ASP.Net forms application, and the file opens without any error as well.
Thank you.
I expect your $http call is missing the response type configuration. This is the way I download office files:
function download(url, defaultFileName) {
var self = this;
var deferred = $q.defer();
$http.get(url, { responseType: "arraybuffer" }).then(
function (data, status, headers) {
var type = headers('Content-Type');
var disposition = headers('Content-Disposition');
if (disposition) {
var match = disposition.match(/.*filename=\"?([^;\"]+)\"?.*/);
if (match[1])
defaultFileName = match[1];
}
defaultFileName = defaultFileName.replace(/[<>:"\/\\|?*]+/g, '_');
var blob = new Blob([data], { type: type });
saveAs(blob, defaultFileName);
deferred.resolve(defaultFileName);
}, function (data, status) {
var e = /* error */
deferred.reject(e);
});
return deferred.promise;
}
I was facing the same error , content was in hexa format, so i added a response type as arraybuffer, problem got resolved. please see below.
$http({
url: '/api/sendPMOToBackendUpdate',
method: "POST",
headers: {'Content-type': 'application/json'},
data: backendTsData,
responseType: 'arraybuffer'
}).success(function(data, status, headers){
var file = new Blob([ data ], { type : 'application/vnd.ms-excel'});
var defaultFileName ="TSC-"+$scope.user.name+"-"+$scope.user.ohrId+".xls";
saveAs(file,defaultFileName);
}).error(function(err) {
console.log('Error: ' + err);
});
You a just need to do one thing only that.
include following js to save file locally. Download it from
"https://github.com/eligrey/FileSaver.js/"
your response data should be in blob type.
I have implemented it and its working.
function downloadfile(url,defaultFileName){
var self = this;
var deferred = $q.defer();
$http.get(url, { responseType: "blob" }).then(
function (data){
saveAs(data.data, defaultFileName)
deferred.resolve(defaultFileName);
}, function (data) {
var e = /* error */
deferred.reject(e);
});
return deferred.promise;
}
I encountered a similar problem when writing excel using Javascript library Excel Builder. At the end, I found the reason was that a control character '\u001a' was included in data.
The solution is to encode the control char in Excel's way as '_x001a_'.
The way I diagnosed the problem was like this:
.xlsx file is just a zipped xml file. You can open it with 7-zip. Inside the xl/ folder there is a file sharedString.xml containing all strings. Extract the file and open it with Notepad++. If you see any control character, then it might be the cause.
first install these module
import * as Excel from 'exceljs';
import * as fs from 'file-saver';
In your function write these
const workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet('sheet');
worksheet.columns = [
{ header: 'Id', key: 'id', width: 10 },
{ header: 'Name', key: 'name', width: 32 }
];
var buff = workbook.xlsx.writeBuffer().then(function (data) {
var blob = new Blob([data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
fs.saveAs(blob, "publications.xlsx");
});
Fs is used to access file system and download file. You can also insert img
https://www.npmjs.com/package/exceljs#images
Related
I need to download an XML received from a controller, but I need the prompt to write a the name and the location where the file will be saved.
First question: is optimal to send the xml like I did in this code or I should send it like a file in some way.
Second question: receiving the xml or the xml file, how can open the prompt and finally save the xml like a file?
[HttpPost]
public ActionResult ExportFiles(string clientName, string currenthcsystem)
{
try
{
var systemActionsConfigurationData = service.GetHcSystemActionsConfigurationData(clientName,currenthcsystem);
XmlSerializer xsSubmit = new XmlSerializer(typeof(HcSystemActionsConfigurationData));
var xml = "";
using (var sww = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sww))
{
xsSubmit.Serialize(writer, systemActionsConfigurationData);
xml = sww.ToString();
}
}
return Content(xml, "text/xml");
}
catch (Exception)
{
throw;
}
}
$('#exportButton').on("click", function () {
var clientName = $(actionsButtons).data('clientname');
var currentHCSystem = $(actionsButtons).data('hcsystemname');
// Create FormData object
var parameters = new FormData();
parameters.append("clientName", clientName);
parameters.append("currentHCSystem", currentHCSystem);
$.ajax({
url: $(actionsButtons).data('export'),
type: 'POST',
data: parameters,
cache: false,
processData: false,
contentType: false,
success: function (response) {
//logic to open the prompt and finally download the xml
},
error: function (err) {
}
});
});
I'm trying to pass javascript objects to my apps script webapp, but I can't figure out how to pass objects that can't be stringified (like a script lock or blob). For example:
Client-side gs:
function testEmail() {
var attachment = DriveApp.getFileById("file id");
var emailBody = 'email body content';
//send email
sendEmailFromClient('sendTo#example.com', "test", emailBody, attachment, 'replyTo#example.com');
}
function sendEmailFromClient(sendTo, title, body, attachments, replyTo) {
var scriptLock = LockService.getScriptLock();
if(!scriptLock.tryLock(8000)){
return;
}
var formData = {
'sendTo': sendTo,
'title': title,
'body' : body,
'attachments': attachments,
'replyTo' : replyTo,
'scriptLock' : scriptLock
};
var options = {
'method': 'post',
'payload': formData
};
Logger.log(UrlFetchApp.fetch('web app url', options).getContentText());
}
Web App gs:
function doPost(event) {
var parameters = event.parameter;
try {
var result = Library['sendEmail'].apply(this, [parameters.sendTo, parameters.title, parameters.body, parameters.attachments, parameters.replyTo]);
parameters.scriptLock.releaseLock();
} catch (error) {
Library.troubleshooting(error);
return ContentService.createTextOutput(error);
}
return ContentService.createTextOutput(result);
}
Library gs:
function sendEmail(sendTo, title, body, attachments, replyTo) {
if (replyTo == undefined) {
replyTo = gv_accountingEmail;
}
if (attachments == undefined) {
attachments = [];
}
var success = false;
try {
GmailApp.sendEmail(
sendTo.valid,
title,
"html content only",
{
attachments: attachments,
replyTo: replyTo,
name: gv_systemBrandName,
htmlBody: body
}
);
success = true;
} catch (error) {
success = error;
} finally {
return success;
}
}
The client grabs the file from their drive, and then sends that info to the web app which then accesses the library to complete the action of sending the email. The web app has special privileges to the library that the client doesn't have which is why I need to send this type of data to it.
If I run the above code from the client, the error that gets logged is "TypeError: parameters.scriptLock.releaseLock is not a function"
If I comment out // parameters.scriptLock.releaseLock();and run it again, I get "Exception: Invalid argument: attachments"
If I turn the attachment into a blob via attachment.getBlob(), then the email sends successfully, but there is nothing attached.
How do we pass these type of objects to web apps?
Thanks!
Try this:
Encode the file first to base-64 web-safe
Send it in your web app using post request
Decode the base-64 web-safe to UTF-8 byte array
Convert it to blob.
Example:
Client Side:
function myFunction(){
var url = "webapp url";
var file = DriveApp.getFileById("file id")
var headers = {
"Authorization": "Bearer " + ScriptApp.getOAuthToken()
};
var fileName = file.getName();
var contentType = file.getBlob().getContentType();
var formData = {
fileName: fileName,
contentType : contentType,
file: Utilities.base64EncodeWebSafe(file.getBlob().getBytes())
};
var options = {
'method' : 'post',
'payload' : formData,
'headers': headers,
};
UrlFetchApp.fetch(url, options);
}
WebApp:
function doPost(e) {
var file = Utilities.newBlob(Utilities.base64DecodeWebSafe(e.parameter.file),e.parameter.contentType,e.parameter.fileName)
Library['sendEmail'].apply(this, [file]);
return ContentService.createTextOutput("test");
}
Library:
function sendEmail(attachments) {
GmailApp.sendEmail(
"recipient email address here",
"Test",
"html content only",
{
attachments: [attachments],
}
);
}
Output:
Reference:
TheMaster answer on Post a file with urlFetchApp to a google web app
Class Utilities
I have a curl command that I want to adapt to javascript using ajax.
curl -v -X 'POST' --data-binary #BinaryData.bin.txt "http://127.0.0.1:3000/api/v1/update_data"
In javascript I used FileReader() and read the file as Text, BinaryString, Array Buffer with different ajax params settings for processData, contentType, cache, etc several times but did not succeed in sending the proper binary string like in python example below.
I tried doing it in python and the following code seems to work as intended:
import requests
import os
path = os.path.normpath('d:/BinaryData.bin.txt')
file = open(path, 'rb')
data = file.read()
r = requests.post("http://127.0.0.1:3000/api/v1/update_data", data=data)
What am I missing in Javascript that it doesn't seem to send the correct data from this file?
Example of how I tried doing it in javascript:
onFileSelected: function(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = (function (file) {
return function(e) {
var data = e.target.result;
$.ajax({
url: "http://127.0.0.1:3000/api/v1/update_data",
data: data,
contentType: 'application/octet-stream',
processData: false,
type: "POST",
success: function () {
// all good
},
error: function() {
// failed
}
});
}
reader.readAsBinaryString(file);
}
i saw this code for downloading a file into client from server...
public ActionResult Download()
{
string file = #"c:\someFolder\foo.xlsx";
string contentType = "application/pdf";
return File(file, controntType, Path.GetFileName(file));
}
But the problem is that i have to do two different things in this Download method.Atfirst ,i want to download the file and then i need to get path of downloaded file and pass in to a documentviewr to display it.so i wrote this code in view razor.
function navigate(target) {
$.ajax({
url: '#Url.Action("Download", "Patient")',
type: 'GET',
dataType: 'json',
cache: false,
data: { 'filepath' : target },
success: function (results) {
event.preventDefault();
documentViewr.loadDocument(results);
},
error: function () {
alert('Error occured');
}
});
}
and in my controller i wrote this:
[HttpGet]
public JsonResult Download(string filepath)
{
var content_type = "";
//path = Path.Combine("D:\file1", filename);
content_type = "application/pdf";
FilePathResult file = File(filepath, content_type,
Path.GetFileName(filepath));
String sDocID, sDocPath;
ViewerController vc = new ViewerController();
sDocPath = Server.MapPath(".") + "\\App_Data\\sample.docx";
sDocID = vc.LoadDocument(sDocPath);
return Json(sDocID, JsonRequestBehavior.AllowGet);
}
so my question is that how i can merge this two actions together?because if i do not return FilePathResult i can not download the file..my another question is that how i can define the path in which file will be downloded?can i tell it to download files into for example Server.MapPath(".") + "\App_Data\...??
i will be grateful if u can help me....
I am attempting to upload an image (an HTML5 canvas converted to an image which is working as intended) to Azure Blob Storage using JavaScript. At this stage, I am less concerned with file size and most concerned with being able to post the file to Azure Blob Storage. I have read a lot of different articles about SAS and for the intents of testing I have a public container, but need to learn how to generate SAS. At this point, I just need to be able to post. I have configured my blob account with CORS as follows:
Allowed Origins: http://localhost:50045
Allowed Methods: DELETE,GET,NONE,POST,PUT
Allowed Headers: content-length,accept,x-ms-*
Exposed Headers: x-ms-*
Max Age (seconds): 10000
When I use the following upload code, which is working to upload to a separate vendors Web Service, I get the following error:
405 (The resource doesn't support specified Http Verb.)
Here is my code:
$scope.reRender = function () {
var canvasImage = document.getElementById("c");
var img = canvasImage.toDataURL("image/png");
var filename = 'Test';
var formdata = new FormData();
formdata.append(filename, img);
var send = function (blob) {
var filename = 'Test.png';
var formdata = new FormData();
formdata.append('File1', blob, filename);
$.ajax({
url: 'http://myblobaccount.blob.core.windows.net/blob1',
type: "POST",
data: formdata,
mimeType: "multipart/form-data",
processData: false,
contentType: false,
crossDomain: true,
success: function (result) {
console.log("Upload to Azure complete!");
},
error: function (error) {
console.log("Something went wrong!");
}
})
}
var canvasImage = document.getElementById("c");
if (!canvasImage.toBlob) {
var dataURL = canvasImage.toDataURL();
var bytes = atob(dataURL.split(',')[1])
var arr = new Uint8Array(bytes.length);
for (var i = 0; i < bytes.length; i++) {
arr[i] = bytes.charCodeAt(i);
}
send(new Blob([arr], { type: 'image/png' }));
}
else
canvasImage.toBlob(send);
}
Does Azure Blob Storage not support a POST? Or is my upload code not in alignment with what Azure is looking for to POST the image file into "blob1"?
Azure blob storage does not support HTML form based file upload, AWS S3 supports this. So POST is not supported by Azure Blob storage. I think it supports PUT, you can try PUT.
But doing all this in JavaScript is dangerous, anyone can put items after acquiring necessary shared signature. If you are not providing shared access signature, blob storage will not allow any operation at all.
Instead you must upload file to your server, PHP, ASP or anything and then connect to Azure via its SDK and save it. Or create a shared signature with rights.
You must get SASToken and add it to your path,
use type: "PUT",
use headers: [{ name: 'x-ms-blob-type', value: 'BlockBlob' }]
Something like this:
<code>
import { Injectable } from '#angular/core';
import { FileUploader, FileItem, Headers } from 'ng2-file-upload';
import { Http } from '#angular/http';
#Injectable()
export class Uploader {
public uploader: FileUploader = new FileUploader({ url: URL, method: 'PUT' });
public hasBaseDropZoneOver: boolean = false;
public files: FileItem[] = this.uploader.queue;
public r: Response;
constructor(private httpClient: Http){}
public getUploader(): FileUploader {
return this.uploader;
}
public fileOverBase(e: any): void {
this.hasBaseDropZoneOver = e;
}
public uploadAll(): void {
this.httpClient.get(getToken)
.subscribe(
result => {
if (result.ok) {
for (var n = 1; n <= this.files.length; n++) {
let fullUrl = URL + this.files[n].file.name + result.json();
console.log('--> send url ' + fullUrl);
this.uploader.setOptions({
url: fullUrl, method: 'PUT',
headers: [{ name: 'x-ms-blob-type', value: 'BlockBlob' }]
});
this.uploader.uploadItem(this.files[n]);
}
}
else {
console.log('--> get token error ' + result.statusText);
}
});
}
public cancelAll(): void {
this.uploader.cancelAll();
}
public clearQueue(): void {
this.uploader.clearQueue();
}
}
</code>
The below is my CORS properties setting which work to me, hope this will be helpful.
Allowed Origins: *
Allowed Methods: GET,PUT
Allowed Headers: *
Exposed Headers: *
Max Age (seconds): 200