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
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 am facing an issue with sending image to javascript adapter from Cordova App using WLResourceRequest with WLResourceRequest.GET
Error:
Response Error :
{"status":414,"statusText":"Request URI too
long","responseText":"Request URI is too long","responseHeaders":
{"cache-control":"no-cache,no-store","content-length":"23","content-
type":"text/html","expires":"now","pragma":"no-
cache"},"errorMsg":"Request URI too long","errorCode":"414"}
Cordova App Source Code:
var invocationData = {
"LOCALE":locale,
"CHANNEL":channel,
"CLIENT_OS":os,
"TYPE":type,
"ISSUE_TYPE":issueType,
"LOCATION":loc,
"CONTRACT_ACC_NO":accNo,
"PHOTOS":photo
};
var resourceRequest = new WLResourceRequest(
"/adapters/Report/makeReport",WLResourceRequest.GET);
resourceRequest.setQueryParameter("params", [invocationData]);
resourceRequest.send().then((response) => {
this.content = response.responseJSON;
deferred.resolve(this.content);
},
function(error){
deferred.reject(error);
}
);
Java Script Adapter Source Code:
function invokeBackend(args, proc){
var path = "SEB-Middleware/api/" + proc;
var input = {
method : 'post',
returnedContentType : 'json',
path : path,
body : {
contentType:"application/json; charset=UTF-8",
content: JSON.stringify(args)
}
};
var response = MFP.Server.invokeHttp(input);
return response ;
}
Above code working successful response without passing image(PHOTOS) parameter,
Also tried below code using POST method :
var formParams = {"params": [invocationData]};
var resourceRequest = new WLResourceRequest(
"/adapters/Report/makeReport",WLResourceRequest.POST;
resourceRequest.sendFormParameters(formParams).then(
function(response) {
console.log('return result');
console.log(JSON.stringify(response));
// success flow
},
function(error) {
// fail flow
console.log('return error');
console.log(JSON.stringify(error));
}
);
Response error: "Error: Invalid invocation of method
WLResourceRequest.sendFormParameters; Form value must be a simple
type.
Another Method:
var formParams = {"params": [JSON.stringify(invocationData)]};
var resourceRequest = new WLResourceRequest("/adapters/Report/makeReport",WLResourceRequest.POST);
resourceRequest.sendFormParameters(formParams).then((response) =>
{
this.content = response.responseJSON;
deferred.resolve(this.content);
},
function(error){
deferred.reject(error);
}
);
Response error: "Error: Invalid invocation of method
WLResourceRequest.sendFormParameters; Form value must be a simple
type.
Please can you advise for how to pass the image(PHOTOS) parameter from cordova app to javascript adapter using WLResourceRequest with POST ?
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
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
I am trying to get a pdf of a div in my view.
I am doing the following:
I get the element, uri encode its html, then pass it to a method via ajax:
AJAX:
function getPDF(html) {
$.ajax({
type: "POST",
url: "#Url.Action("printPage")",
data: { html: encodeURIComponent(html) }
}).done(function (result) {
window.open("data:application/pdf; " + result);
$("#printArea").html(result);
}).fail(function (data) {
alert("Failed");
});
}
Method:
[HttpPost]
public void printPage(string html)
{
String decoded = System.Uri.UnescapeDataString(html);
var cd = new System.Net.Mime.ContentDisposition
{
FileName = "something.pdf",
Inline = false
};
Response.AppendHeader("Content-Disposition", cd.ToString());
var mem = Bcs.Common.Utilities.HTMLtoPDF.getPDF(decoded);
//var base64EncodedPDF = System.Convert.ToBase64String(pdfByteArray);
Response.BinaryWrite(mem.ToArray());
//return File(mem, System.Net.Mime.MediaTypeNames.Application.Octet);
}
In the end I get a popup to open a pdf but it won't open, according to adobe acrobat it is corrupt.
I tried sending the html as a perameter to the method, but the perameter is too long
HTTP Error 404.15 - Not Found The request filtering module is configured to deny a request where the query string is too long.
What would be a good way of doing this.
public JsonResult printPage(String html)
{
String decoded = System.Uri.UnescapeDataString(html);
var cd = new System.Net.Mime.ContentDisposition
{
FileName = "something.pdf",
Inline = false
};
var mem = Bcs.Common.Utilities.HTMLtoPDF.getPDF(decoded);
mem.Flush();
mem.Position = 0;
String b64Converted = Convert.ToBase64String(mem.ToArray());
return Json(b64Converted, JsonRequestBehavior.AllowGet );
System.Net.Mime.MediaTypeNames.Application.Octet);
}
Then in the view:
$.ajax({
type: "POST",
url: "#Url.Action("printPage")",
data: { html: encodeURIComponent(html) },
}).done(function (result) {
$("#printArea").append('PDF');
}).fail(function (data) {
alert("Failed");
});
Apparently its very easy
Just base64 the pdf and send it over at the json response.