I'm using exceljs to generate an excel file(using its streaming capabilities). Instead of saving the file on the server and then reading and returning it, I want to directly stream the exceljs stream to the response in hapijs.
function(request, reply) {
const options = {
stream: reply, // I need to somehow declare the reply stream here
useStyles: false,
useSharedStrings: true
};
const workbook = new Excel.stream.xlsx.WorkbookWriter(options);
workbook.addWorksheet('Test');
const worksheet = workbook.getWorksheet('Test');
worksheet.columns = [{
header: 'product', key: 'product'
}];
worksheet.addRow({product: 'MyProduct'}).commit();
worksheet.commit();
workbook.commit();
Is this possible? I cannot directly pass reply to the options of the Exceljs stream as I would do in expressjs? Any thoughts?
var wb = new Excel.stream.xlsx.WorkbookWriter();
var sh = wb.addWorksheet('sheet1');
sh.columns = [
{ header: 'Name', key: 'name' },
{ header: 'Age', key: 'age' }
];
sh.addRow(['Ben', 8]);
sh.addRow(['Dave', 10]);
sh.commit();
wb.commit()
.then(function () {
var stream = wb.stream;
var b = stream.read();
reply(b)
.type('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
.header('Content-Disposition', 'attachment; filename=file.xlsx')
.header('Content-Length', stream.length);
});
Related
Due to the nature of my project I have a image dataURL (NOT an actual image file) that I am trying to upload to IPFS via Pinata SDK. I have converted the image dataURL into a buffer(array) and tried 2 different methods but none of them works. Here is my code:
SAMPLE 1
var myBlob = new Blob([new Uint8Array(myBuffer)]);
var myReadableStream = myBlob.stream()
pinata.pinFileToIPFS(myReadableStream)
ERROR: Unhandled Rejection (TypeError): source.on is not a function
SAMPLE 2
var myBlob = new Blob([new Uint8Array(myBuffer)]);
var myHeaders = new Headers();
myHeaders.append("pinata_api_key", "MY_KEY");
myHeaders.append("pinata_secret_api_key", "MY_SECRET_KEY");
var formdata = new FormData();
formdata.append("test", myBlob);
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: formdata,
redirect: 'follow'
};
fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", requestOptions)
.then(response => response.text())
.then(result => console.log('result',result))
.catch(error => console.log('error', error));
ERROR: 400 Bad Request, result {"error":"Unexpected field"}
with buffers things can be a little tricky. You'll need to format your request in a slightly different way.
I would take a look at this code snippet for an example of how somebody got this to work:
const pinataSDK = require("#pinata/sdk");
const pinata = pinataSDK(
"Pinata API Key",
"Pinata API Secret"
);
const { fs, vol } = require("memfs");
(async () => {
try {
const base64 = "base64 file string";
const buf = Buffer.from(base64, "base64");
memfs.writeFileSync("File Name", buf);
const read = vol.createReadStream("File Name");
const res = await pinata.pinFileToIPFS(read);
console.log(res);
} catch (error) {
console.log(error);
}
})();
I am currently a developing an application in MVC Core that is using a PDFTron webviewer. Is there anyway to save the edited pdf edited with pdftron webviewer to the server?
There is a feature of pdftron that saves annotations to the server, but I need to save the whole pdf with the edits to the server.
WebViewer({
path: '/lib/WebViewer',
initialDoc: '/StaticResource/Music.pdf', fullAPI: !0, enableRedaction: !0
}, document.getElementById('viewer')).then(
function(t) {
samplesSetup(t);
var n = t.docViewer;
n.on('documentLoaded', function() {
document.getElementById('apply-redactions').onclick = function() {
t.showWarningMessage({
title: 'Apply redaction?',
message: 'This action will permanently remove all items selected for redaction. It cannot be undone.',
onConfirm: function () {
alert( );
t.docViewer.getAnnotationManager().applyRedactions()
debugger
var options = {
xfdfString: n.getAnnotationManager().exportAnnotations()
};
var doc = n.getDocument();
const data = doc.getFileData(options);
const arr = new Uint8Array(data);
const blob = new Blob([arr], { type: 'application/pdf' });
const data = new FormData();
data.append('mydoc.pdf', blob, 'mydoc.pdf');
// depending on the server, 'FormData' might not be required and can just send the Blob directly
const req = new XMLHttpRequest();
req.open("POST", '/DocumentRedaction/SaveFileOnServer', true);
req.onload = function (oEvent) {
// Uploaded.
};
req.send(data);
return Promise.resolve();
},
});
};
}),
t.setToolbarGroup('toolbarGroup-Edit'),
t.setToolMode('AnnotationCreateRedaction');
}
);
When i send the request to the Controller i am not getting the file it is coming null
[HttpPost]
public IActionResult SaveFileOnServer(IFormFile file)
{
return Json(new { Result="ok"});
}
Can any one suggest me where i am going wrong
Thanks in adavance
For JavaScript async function, you need to wait for it completes before doing other things. For example, AnnotationManager#applyRedactions() returns a Promise, the same for AnnotationManager#exportAnnotations() and Document#getFileData().
For JS async functions, you can take a look at:
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
So here you may want to use await to wait for the Promise completes.
Currently I am able to record user input, pass the recording URL to the needed function, and download the audio file locally. What I am trying to do with the audio file is either get a buffer of it to send to Lex or convert it to the format Lex needs.
Per AWS Documentation the following values are accepted for the input stream param value:
var params = {
botAlias: 'STRING_VALUE', /* required */
botName: 'STRING_VALUE', /* required */
contentType: 'STRING_VALUE', /* required */
inputStream: new Buffer('...') || 'STRING_VALUE' || streamObject, /*required */
userId: 'STRING_VALUE', /* required */
accept: 'STRING_VALUE',
requestAttributes: any /* This value will be JSON encoded on your behalf with JSON.stringify() */,
sessionAttributes: any /* This value will be JSON encoded on your behalf with JSON.stringify() */
};
lexruntime.postContent(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
Per the twilio documentation it looks like the audio file is pretty flexible...
A request to the RecordingUrl will return a recording in binary WAV audio format by default. To request the recording in MP3 format, append ".mp3" to the RecordingUrl.
What do I need to do to get the twilio recorded audio in the right format for Lex? Is it just a matter of building the correct Lex param set or do I need to do some audio conversion before hand? I am writing this application in node js if that helps and I can add more code if it will help.
I was able to figure this out by downloading the file from Twilio as a PCM and changing my parameters a bit. Also, due to the way that Twilio handles the record verb, I needed to transfer the call to a hold state while waiting for the recordingStatusCallback to POST out. I also send a text to the caller with the final status from Lex.
The code I used to download the file:
app.post('/processRecording', (request, response) => {
var https = require('https');
var fs = require('fs');
let callSID = request.body.CallSid;
let url = request.body.RecordingUrl;
var saveFile = new Promise(function(resolve, reject) {
let fileName = callSID+ ".pcm";
var file = fs.createWriteStream(fileName);
var request = https.get(url, function(response) {
response.pipe(file);
resolve();
});
});
});
const accountSid = 'YOUR ACCOUNT SID';
const authToken = 'YOUR AUTH TOKEN';
const client = require('twilio')(accountSid, authToken);
//Once the file is downloaded, I then fetch the call from the hold state using this code:
saveFile.then(function(){
client.calls(callSID)
.update({method: 'POST', url: '/updateCall'})
.then(call => console.log(call.to))
.done();
});
And my updateCall endpoint looks like this:
app.post('/updateCall', (request, response) => {
let lexruntime = new AWS.LexRuntime();
let recordedFileName = request.body.CallSid + '.pcm';
let toNumber = request.body.To;
let fromNumber = request.body.From;
let twiml = new Twilio.twiml.VoiceResponse();
let lexFileStream = fs.createReadStream(recordedFileName);
let sid = request.body.CallSid;
var params = {
botAlias: 'prod', /* required */
botName: 'OrderFlowers', /* required */
contentType: 'audio/lpcm; sample-rate=8000; sample-size-bits=16; channel-count=1; is-big-endian=false',
accept: 'text/plain; charset=utf-8',
userId: sid /* required */
};
params.inputStream = lexFileStream;
lexruntime.postContent(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
if (data.dialogState == "ElicitSlot" || data.dialogState == "ConfirmIntent" || data.dialogState == "ElicitIntent" ){
twiml.say(data.message);
twiml.redirect({
method: 'POST'
}, '/recordVoice');
response.type('text/xml');
response.send(twiml.toString());
}
else if (data.dialogState == "Fulfilled" ){
twiml.say(data.message);
response.type('text/xml');
response.send(twiml.toString());
client.messages.create({
to: toNumber,
from: fromNumber,
body: data.message
}).then(msg => {
}).catch(err => console.log(err));
}
else{
twiml.say(data.message);
response.type('text/xml');
response.send(twiml.toString());
}
});
});
The recordVoice endpoint is actually a Twilio Serverless Function But I think this is what it would look like as an express endpoint:
app.post('/recordVoice', (request, response) => {
let twiml = new Twilio.twiml.VoiceResponse();
twiml.record({
action: '/deadAir',
recordingStatusCallback: '/processRecording',
trim: true,
maxLength: 10,
finishOnKey: '*'
});
twiml.say('I did not receive a recording');
response.type('text/xml');
response.send(twiml.toString());
});
The /deadAir endpoint is also a Twilio Serverless Function but this is what it would look like:
app.post('/deadAir', (request, response) => {
let twiml = new Twilio.twiml.VoiceResponse();
twiml.pause({
length: 60
});
response.type('text/xml');
response.send(twiml.toString());
});
This is the function I am using to upload file but is is giving me the error : Length is undefined. what I have to change in this code. where to give path of file to upload.
fileChange(event) {
let fileList: FileList = event.target.files;
if(fileList) {
let file: File = fileList[0];
let formData:FormData = new FormData();
formData.append('uploadFile', file, file.name);
let headers = new Headers();
/** No need to include Content-Type in Angular 4 */
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = new RequestOptions({ headers: headers });
this.http.post(`assets/Files/info.txt`, formData, options)
.map(res => res.json())
.catch(error => Observable.throw(error))
.subscribe(
data => console.log(fileList),
error => console.log(error)
)
}
}
you need to use xhr request to transfer files
fileChange(event: EventTarget) {
let eventObj: MSInputMethodContext = <MSInputMethodContext> event;
let target: HTMLInputElement = <HTMLInputElement> eventObj.target;
let files: FileList = target.files;
if(files) {
let file: File = files[0];
this.upload(file)
}
}
public upload(filedata: File) {
let url = 'your url'
if (typeof filedata != 'undefined') {
return new Promise((resolve, reject) => {
let formData: any = new FormData();
let xhr = new XMLHttpRequest();
formData.append('icondata', filedata, filedata.name);
xhr.open('POST', url, true);
xhr.setRequestHeader('Authorization', 'JWT ' + localStorage.getItem('id_token'));
xhr.send(formData);
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
resolve(JSON.parse(xhr.responseText));
}
}
});
}
}
I understand that this is not the functionality you want to have but with no backend you can not upload files to be persistent, they should be stored somewhere. If you just wanna manipulate file names for instance, skip the express part in my answer. I personally used this code which I altered to upload multiple files.
In your Component :
import {FormArray, FormBuilder, FormControl, FormGroup} from "#angular/forms";
declare FormBuilder in the constructor:
constructor (private http: Http, private fb: FormBuilder) {}
in ngOnInit() set a variable as follows :
this.myForm = this.fb.group({chosenfiles: this.fb.array([])});
this is the code for the upload method :
// invoke the upload to server method
// TODO
// Should be in a service (injectable)
upload() {
const formData: any = new FormData();
const files: Array<File> = this.filesToUpload;
//console.log(files);
const chosenf = <FormArray> this.myForm.controls["chosenfiles"];
// iterate over the number of files
for(let i =0; i < files.length; i++){
formData.append("uploads[]", files[i], files[i]['name']);
// store file name in an array
chosenf.push(new FormControl(files[i]['name']));
}
this.http.post('http://localhost:3003/api/upload', formData)
.map(files => files.json())
.subscribe(files => console.log('upload completed, files are : ', files));
}
the method responsible for the file change :
fileChangeEvent(fileInput: any) {
this.filesToUpload = <Array<File>>fileInput.target.files;
const formData: any = new FormData();
const files: Array<File> = this.filesToUpload;
console.log(files);
const chosenf = <FormArray> this.myForm.controls["chosenfiles"];
// iterate over the number of files
for(let i =0; i < files.length; i++){
formData.append("uploads[]", files[i], files[i]['name']);
// store file name in an array
chosenf.push(new FormControl(files[i]['name']));
}
}
Template is something like this
<input id="cin" name="cin" type="file" (change)="fileChangeEvent($event)" placeholder="Upload ..." multiple/>
Notice multiple responsible for allowing multiple selections
The express API which will handle the request uses multer after an npm install
var multer = require('multer');
var path = require('path');
specify a static directory which will hold the files
// specify the folder
app.use(express.static(path.join(__dirname, 'uploads')));
As specified by multer
PS: I did not investigate multer, as soon as i got it working, i moved to another task but feel free to remove unnecessary code.
var storage = multer.diskStorage({
// destination
destination: function (req, file, cb) {
cb(null, './uploads/')
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
var upload = multer({ storage: storage });
And finally the endpoint
app.post("/api/upload", upload.array("uploads[]", 12), function (req, res) {
console.log('files', req.files);
res.send(req.files);
});
I have the follow problem: I need to consume a REST service (3rd party, not mine) and show the result data to the user using an Ext.grid.Panel.
The problem is I have no idea of the data structure and content ( it is a JSON from Geoserver's queryLayer ) so I can't have a store/grid/model field definition to respect the ExtJS MVC design.
So how can I be more flexible in this situation? I try to add a row to the grid by hand but after read https://www.sencha.com/forum/showthread.php?48625-how-can-i-insert-a-row-in-GRID I think it is a kind of crime to do
You can add a conversion layer for dynamic fields in the model class. The conversion will provide a string readable format for data you don't know the structure.
Ext.define('AppName.DynamicRow', {
extend: 'Ext.data.Model',
fields: [{
name: 'fixed1',
type: 'string'
}, {
name: 'fixed2',
type: 'string'
}, {
name: 'dynamic',
type: 'string',
calculate: function (data) {
Ext.Object.getAllKeys(data)
.map(function(key) {
return key + ': ' + data[key];
})
.join(', ');
}
}]
});
Then you will show all unstructured data in a grid column simply adding 'dynamic' field as dataIndex.
My workaround:
First, receive the data using a function to concentrate all creation stuff:
function addGrid ( title, data ) {
var storeColumns = getStoreColumnsFromJson( data[0] );
var gridColumns = getGridColumnsFromJson( data[0] );
var store = createStore( data, storeColumns );
var grid = createGrid( title, store, gridColumns );
myContainerWindowPanel.add( grid );
}
Now, I need to take a data sample (first row) to get the column names from the JSON data to the grid and its store:
function getStoreColumnsFromJson ( obj ) {
var keys = [];
for (var key in obj) {
if ( obj.hasOwnProperty(key) ) {
keys.push({name : key});
}
}
return keys;
}
function getGridColumnsFromJson ( obj ) {
var keys = [];
for (var key in obj) {
if ( obj.hasOwnProperty(key) ) {
keys.push({text: key, dataIndex: key});
}
}
return keys;
}
Now I'll create the grid and the store. I will not use the Model simply because this worked without it. If someone have a strong advice to create the model I'll appreciate.
function createGrid ( title, store, columnNames ) {
var dummyGrid = Ext.create('Ext.grid.Panel', {
border: false,
title : title,
store : store,
frame: false,
margin: "10 0 0 0",
flex:1,
loadMask: true,
columns:columnNames,
autoHeight:true
});
return dummyGrid;
}
function createStore ( storeData, columns ) {
var arrData = [];
var theData = storeData;
if ( !$.isArray( storeData ) ) {
arrData.push( storeData );
theData = arrData;
}
var store = Ext.create('Ext.data.Store',{
fields: columns,
autoLoad: true,
data: theData
});
return store;
}