I need to add an file attachment to a soap request from node.js application.
I am able to send request with node-soap library, and now I need to add a file to the request.
I did it with a java client or with soapUI, but I have to do it in node.js, maybe it's possible to do that overriding default request object ?
I didn't find a solution with node-soap, I had to build manually my soap request :
var soapHeader = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header/><soapenv:Body><ws:method>';
var soapFooter = '</ws:method></soapenv:Body></soapenv:Envelope>';
function sendSoapRequestWithAttachments(soap,files){
var soapRequest = jsonToXml.buildObject(mail);
var finalSoapRequest = soapHeader + soapRequest + soapFooter;
var multipartMail = [];
// Add soap request
multipartMail.push({
'Content-Type': 'text/xml; charset=utf-8',
body: finalSoapRequest
});
// Add attachments
if (files) {
files.forEach(function (file) {
multipartMail.push({
'Content-Id': '<' + file.uuid + '>',
'Content-Type': 'application/octet-stream',
'Content-Transfer-Encoding': 'binary',
body: fs.createReadStream(file.path)
});
});
}
var options = {
uri: URL,
method: 'POST',
multipart: multipartMail
};
request.post(options, function (error, response) {
...
}
}
I found a way to send attachment using node-soap using base64 encoding
here is an example
import soap from 'soap'
import fs from 'fs'
async function main(){
const filepath = '/path/to/attachment/file.extension'
const client = await soap.createClientAsync(/* ... */)
const result = await client.AnApiMethodAsync({
expectedKeyForTheFile: await fs.readFileAsync(filepath, 'base64')
})
}
main()
Related
i'm trying to upload my files as form-data, after i've created a scene. But I receive always the error "Specified Photoscene ID doesn't exist in the database" (which were created directly before).
My upload function:
// Upload Files
async function uploadFiles(access_Token, photoSceneId, files) {
try {
const params = new URLSearchParams({
'photosceneid': photoSceneId,
'type': 'image',
'file': files
})
const headers = Object.assign({
Authorization: 'Bearer ' + access_Token,
'Content-Type': 'multipart/form-data' },
files.getHeaders()
)
let resp = await axios({
method: 'POST',
url: 'https://developer.api.autodesk.com/photo-to-3d/v1/file',
headers: headers,
data: params
})
let data = resp.data;
return data;
} catch (e) {
console.log(e);
}
};
I've also tried a few varaints, e.g. adding the photosceneId to the form data (form.append(..), but doesn't works either.
Any helpful suggestion are appreciated. Thx in advance.
There might be two problems here.
First, I am not sure of it, as I don't have experience of URLSearchParams as a "packer" for POST requests. This might be the reason why you get "Specified Photoscene ID doesn't exist in the database" error - perhaps the way the data are serialized using URLSearchParams is not compatible.
The second problem, I am sure of it, is regarding the way you submit the files.
According to documentation, you have to pass the files one by one, like
"file[0]=http://www.autodesk.com/_MG_9026.jpg" \
"file[1]=http://www.autodesk.com/_MG_9027.jpg"
and not just passing an array to the "file" field.
Having this said, try this approach:
var axios = require('axios');
var FormData = require('form-data');
var fs = require('fs');
var data = new FormData();
var TOKEN = 'some TOKEN';
const photoSceneID = 'some_photoscene_id';
data.append('photosceneid', photoSceneID);
data.append('type', 'image');
data.append('file[0]', fs.createReadStream('/C:/TEMP/Example/DSC_5427.JPG'));
data.append('file[1]', fs.createReadStream('/C:/TEMP/Example/DSC_5428.JPG'));
data.append('file[2]', fs.createReadStream('... and so on ...'));
var config = {
method: 'post',
url: 'https://developer.api.autodesk.com/photo-to-3d/v1/file',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + TOKEN,
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
Also, I always recommend instead of jumping right into the code, to check first the workflow using apps like Postman or Insomnia and then, after you validated the workflow (created the photoscene, all images were properly uploaded and so on), you can translate this into the code.
At the end of this blogpost you will find the link to alrady created Postman collection, but I highly recommend building your own collection, as part of the learning step.
This is the solution that worked for me. Please note that the upload should be limited by a maximum of 20 files per call.
// Upload Files
async function uploadFiles(access_Token, photoSceneId) {
try {
let dataPath = path.join(__dirname, '../../data')
let files = fs.readdirSync(dataPath)
var data = new FormData();
data.append('photosceneid', photoSceneId)
data.append('type', 'image')
for(let i=0; i < files.length; i++) {
let filePath = path.join(dataPath, files[i])
let fileName = 'file[' + i + ']'
data.append(fileName, fs.createReadStream(filePath))
}
const headers = Object.assign({
Authorization: 'Bearer ' + access_Token,
'Content-Type': 'multipart/form-data;boundary=' + data.getBoundary()},
data.getHeaders()
)
let resp = await axios({
method: 'POST',
url: 'https://developer.api.autodesk.com/photo-to-3d/v1/file',
headers: headers,
maxContentLength: Infinity,
maxBodyLength: Infinity,
data: data
})
let dataResp = resp.data;
return dataResp;
} catch (e) {
console.log(e);
}
};
I am using node js for testing apis. In 1 API i need to upload a file. I am not able to attach file in the payload. I am able to do it in postman but not do it with nodejs. I am using request-promise for managing requests. How can i go about it?
Thanks.
Slight modification in the #rustam's answer,
return request({
method: 'POST',
uri: 'http://localhost:8333',
formData ={
'file': fileStream,
}
});
If you just need to specify the file in payload, then please look at this invented example:
const fs = require('fs');
const request = require('request-promise')
function sendFile() {
const readStream = fs.createReadStream('./test.txt');
return request({
method: 'POST',
uri: 'http://localhost:8333',
body: readStream
});
}
async function main() {
const response = await sendFile();
console.log({ response });
}
main();
In this example, I hard-coded the file name and url, but for you this may be function parameters.
I have an API endpoint that lets the client post their csv to our server then post it to someone else server. I have done our server part which save uploaded file to our server, but I can't get the other part done. I keep getting error { message: 'File not found', code: 400 } which may mean the file never reach the server. I'm using axios as an agent, does anyone know how to get this done? Thanks.
// file = uploaded file
const form_data = new FormData();
form_data.append("file", fs.createReadStream(file.path));
const request_config = {
method: "post",
url: url,
headers: {
"Authorization": "Bearer " + access_token,
"Content-Type": "multipart/form-data"
},
data: form_data
};
return axios(request_config);
Update
As axios doc states as below and the API I'm trying to call requires a file
// data is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', and 'PATCH'
// When no transformRequest is set, must be of one of the following types:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - Browser only: FormData, File, Blob
// - Node only: Stream, Buffer
Is there any way to make axios send a file as a whole? Thanks.
The 2 oldest answers did not work for me. This, however, did the trick:
const FormData = require('form-data'); // npm install --save form-data
const form = new FormData();
form.append('file', fs.createReadStream(file.path));
const request_config = {
headers: {
'Authorization': `Bearer ${access_token}`,
...form.getHeaders()
}
};
return axios.post(url, form, request_config);
form.getHeaders() returns an Object with the content-type as well as the boundary.
For example:
{ "content-type": "multipart/form-data; boundary=-------------------0123456789" }
I'm thinking the createReadStream is your issue because its async. try this.
Since createReadStream extends the event emitter, we can "listen" for when it finishes/ends.
var newFile = fs.createReadStream(file.path);
// personally I'd function out the inner body here and just call
// to the function and pass in the newFile
newFile.on('end', function() {
const form_data = new FormData();
form_data.append("file", newFile, "filename.ext");
const request_config = {
method: "post",
url: url,
headers: {
"Authorization": "Bearer " + access_token,
"Content-Type": "multipart/form-data"
},
data: form_data
};
return axios(request_config);
});
This is what you really need:
const form_data = new FormData();
form_data.append("file", fs.createReadStream(file.path));
const request_config = {
headers: {
"Authorization": "Bearer " + access_token,
"Content-Type": "multipart/form-data"
},
data: form_data
};
return axios
.post(url, form_data, request_config);
In my case, fs.createReadStream(file.path) did not work.
I had to use buffer instead.
const form = new FormData();
form.append('file', fs.readFileSync(filePath), fileName);
const config = {
headers: {
Authorization: `Bearer ${auth.access_token}`,
...form.getHeaders(),
},
};
axios.post(api, form.getBuffer(), config);
I have made an interceptor you can connect to axios to handle this case in node: axios-form-data. Any feedback would be welcome.
npm i axios-form-data
example:
import axiosFormData from 'axios-form-data';
import axios from 'axios';
// connect axiosFormData interceptor to axios
axios.interceptors.request.use(axiosFormData);
// send request with a file in it, it automatically becomes form-data
const response = await axios.request({
method: 'POST',
url: 'http://httpbin.org/post',
data: {
nonfile: 'Non-file value',
// if there is at least one streamable value, the interceptor wraps the data into FormData
file: createReadStream('somefile'),
},
});
// response should show "files" with file content, "form" with other values
// and multipart/form-data with random boundary as request header
console.log(response.data);
I had a same issue, I had a "pdf-creator-service" for generate PDF document from html.
I use mustache template engine for create HTML document - https://www.npmjs.com/package/mustache
Mustache.render function returns html as a string what do I need to do to pass it to the pdf-generator-service ? So lets see my suggestion bellow
//...
async function getPdfDoc(props: {foo: string, bar: string}): Promise<Buffer> {
const temlateFile = readFileSync(joinPath(process.cwd(), 'file.html'))
mustache.render(temlateFile, props)
const readableStream = this.getReadableStreamFromString(htmlString)
const formData = new FormData() // from 'form-data'
formData.append('file', options.file, { filename: options.fileName })
const formHeaders = formData.getHeaders()
return await axios.send<Buffer>(
{
method: 'POST',
url: 'https://pdf-generator-service-url/pdf',
data: formData,
headers: {
...formHeaders,
},
responseType: 'arraybuffer', // ! important
},
)
}
getReadableStreamFromString(str: string): Readable {
const bufferHtmlString = Buffer.from(str)
const readableStream = new Readable() // from 'stream'
readableStream._read = () => null // workaround error
readableStream.push(bufferHtmlString)
readableStream.push(null) // mark end of stream
return readableStream
}
For anyone who wants to upload files from their local filesystem (actually from anywhere with the right streams architecture) with axios and doesn't want to use any external packages (like form-data).
Just create a readable stream and plug it right into axios request function like so:
await axios.put(
url,
fs.createReadStream(path_to_file)
)
Axios accepts data argument of type Stream in node context.
Works fine for me at least in Node v.16.13.1 and with axios v.0.27.2
I am using npm request module (https://www.npmjs.com/package/request) to post binary content to a servlet. The binary content is received as part of http request, using the npm request module it is then posted to the J2ee server.
Along with the post, I need to pass some custom headers. I am using the below code to do that
var req = require('request');
function upload(request, response) {
var options = {
headers: {
'customheader1': 'val1',
'customheader2': 'val2'
}
};
var target = req.post('http://'+host+':'+port+'/myapp/Upload', options);
request.pipe(target);
target.on('finish', function() {
console.log('Uploaded with headers');
})
}
However, the headers are going as blank to the server. What would be the right way to pass headers using request.post?
As per request Docs (http://github.com/request/request)
var req = require('request');
function upload(request, response) {
var options = {
url: 'http://'+host+':'+port+'/myapp/Upload',
headers: {
'customheader1': 'val1',
'customheader2': 'val2'
}
};
var target = req.post( options, function(err,data){
console.log('uploaded with headers')
})
request.pipe(target);
}
I have used the Winston module to create a daily log file for my offline app. I now need to be able to send or upload that file to a remote server via POST (that part already exists)
I know I need to write the file in chunks so it doesn't hog the memory so I'm using fs.createReadStream however I seem to only get a 503 response, even if sending just sample text.
EDIT
I worked out that the receiver was expecting the data to be named 'data'. I have removed the createReadSteam as I could only get it to work with 'application/x-www-form-urlencoded' and a synchronous fs.readFileSync. If I change this to 'multipart/form-data' on the php server would I be able to use createReadStream again, or is that only if I change to physically uploading the json file.
I've only been learning node for the past couple of weeks so any pointers would be gratefully received.
var http = require('http'),
fs = require('fs');
var post_options = {
host: 'logger.mysite.co.uk',
path: '/',
port: 80,
timeout: 120000,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
var sender = http.request(post_options, function(res) {
if (res.statusCode < 399) {
var text = ""
res.on('data', function(chunk) {
text += chunk
})
res.on('end', function(data) {
console.log(text)
})
} else {
console.log("ERROR", res.statusCode)
}
})
var POST_DATA = 'data={['
POST_DATA += fs.readFileSync('./path/file.log').toString().replace(/\,+$/,'')
POST_DATA += ']}'
console.log(POST_DATA)
sender.write(POST_DATA)
sender.end()
After gazillion of trial-failure this worked for me. Using FormData with node-fetch. Oh, and request deprecated two days ago, btw.
const FormData = require('form-data');
const fetch = require('node-fetch');
function uploadImage(imageBuffer) {
const form = new FormData();
form.append('file', imageBuffer, {
contentType: 'image/jpeg',
filename: 'dummy.jpg',
});
return fetch(`myserver.cz/upload`, { method: 'POST', body: form })
};
In place of imageBuffer there can be numerous things. I had a buffer containing the image, but you can also pass the result of fs.createReadStream('/foo/bar.jpg') to upload a file from drive.
copied from https://github.com/mikeal/request#forms
var r = request.post('http://service.com/upload', function optionalCallback (err, httpResponse, body) {
if (err) {
return console.error('upload failed:', err);
}
console.log('Upload successful! Server responded with:', body);
})
var form = r.form()
form.append('my_field1', 'my_value23_321')
form.append('my_field2', '123123sdas')
form.append('my_file', fs.createReadStream(path.join(__dirname, 'doodle.png')))
Have a look at the request module.
It will provide you the ability to stream a file to POST requests.