I have a working get request. I took it from stackoverflow. When browser make get request to the express node js server video starts playing. But i want to change it as a post request because i want to choose which video i want. So i want to change players video without refreshing the page. I changed this method to post and i added body-parser to it. Here my method :
app.post('/api/video', urlencodedParser , function(req, res) {
var folder = req.body.folder
var path = 'D:/VideoDirectory/'+ folder + '/clip.mp4'
const stat = fs.statSync(path)
const fileSize = stat.size
const range = req.headers.range
if (range) {
const parts = range.replace(/bytes=/, "").split("-")
const start = parseInt(parts[0], 10)
const end = parts[1]
? parseInt(parts[1], 10)
: fileSize-1
const chunksize = (end-start)+1
const file = fs.createReadStream(path, {start, end})
const head = {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4',
}
res.writeHead(206, head)
file.pipe(res)
} else {
const head = {
'Content-Length': fileSize,
'Content-Type': 'video/mp4',
}
res.writeHead(200, head)
fs.createReadStream(path).pipe(res)
}
})
Here my ajax post request :
var folder = {folder: "testFolder"}
$.ajax({
type: 'POST',
url: '/api/video',
data: folder,
success: function(data){
alert('post request sent');
}
});
}
After i make this post request video is coming to browser. I know that because internet download manager try to catch it. It's have correct file size. But this video doesn't go to the html5 player. How can i feed the player with this post response ? And i want to change video without refresing the page. Here my html5 video player code :
<video id="videoPlayer" controls>
<source src="http://xxx.xxx.xx.xx:4000/api/video" type="video/mp4">
</video>
Thanks to #btzr i used get request with parameters. Here it's last form :
app.get('/api/video/:folder' , function(req, res) {
var streamer = req.params.folder
const path = 'D:/VideoDirectory/' + folder+ '/clip.mp4'
const stat = fs.statSync(path)
const fileSize = stat.size
const range = req.headers.range
if (range) {
const parts = range.replace(/bytes=/, "").split("-")
const start = parseInt(parts[0], 10)
const end = parts[1]
? parseInt(parts[1], 10)
: fileSize-1
const chunksize = (end-start)+1
const file = fs.createReadStream(path, {start, end})
const head = {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4',
}
res.writeHead(206, head)
file.pipe(res)
} else {
const head = {
'Content-Length': fileSize,
'Content-Type': 'video/mp4',
}
res.writeHead(200, head)
fs.createReadStream(path).pipe(res)
}
})
And i'm just changing video player's src with javascript:
var player = document.getElementById('videoPlayer');
player.src = 'http://xxx.xxx.xx.xx:4000/api/video/' + folder;
Video player making get request to the server when src updated.
Related
I just started learning MERN and was building a video streaming platform.
For showing a sample video as stream , I wrote the following code on node js (with express):
app.get("/video", (req, res) => {
// const filePath = path.join(__dirname, "assest", "sample.mp4");
const filePath = "assest/sample.mp4";
const stat = fs.statSync(filePath);
const size = stat.size;
const range = req.headers.range;
if (range) {
const parts = range.replace(/bytes=/, "").split("-");
const start = parseInt(parts[0], 10);
const end = parts[1] ? parseInt(parts[1], 10) : size - 1;
const c = end - start;
const chunkSize = c + 1;
const header = {
"Content-Range": `bytes ${start}-${end}/${size}`,
"Accept-Ranges": "bytes",
"Content-Length": chunkSize,
"Content-Type": "video/mp4",
};
const fileStream = fs.createReadStream(filePath, { start, end });
res.writeHead(204, header);
fileStream.pipe(res);
} else {
const header = {
"Content-Length": size,
"Content-Type": "video/mp4",
};
res.writeHead(200, header);
fs.createReadStream(filePath).pipe(res);
}
});
Here is the github repo for folder structure and additional code
And on the client site to display it, I'm calling the api something like this:
<video
controls
autoPlay
style={{ width: "400px" }}
src="http://localhost:8080/video"
type="video/mp4"
></video>
But the video is not playing.
If I sent the whole video as a static file , like this res.sendFile(path.join(__dirname, "assest", "sample.mp4")); , it's working.
Can someone correct me here?
Basically, i have video background in my website, loops infinitely. To decrease initial load, i streamed the video. The website is built in Next.js.
<video autoPlay loop muted playsInline preload='none'>
<source src='/api/video' type='video/webm' />
</video>
// pages/api/video
const range = req.headers.range || 'bytes=0-'
const fileName = 'video.webm'
const videoPath = path.resolve('public/assets/', fileName)
const videoSize = fs.statSync(videoPath).size
const chunkSize = 1 * 1e6
const start = Number(range.replace(/\D/g, ''))
const end = Math.min(start + chunkSize, videoSize - 1)
const contentLength = end - start + 1
const headers = {
'Content-Range': `bytes ${start}-${end}/${videoSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': contentLength,
'Content-Type': 'video/mp4',
}
res.writeHead(206, headers)
const stream = fs.createReadStream(videoPath, { start, end })
stream.pipe(res)
Everything is working fine. But i realized that video is again being downloaded for the next loops. So i want it to download initially and when it loops next time, i want (to maybe cache it) so that next loops it doesn't download again.
it occurs to me too. lucky for me. i find a solution.
var xhr = new XMLHttpRequest();
xhr.open('POST', './play', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {//请求成功
var blob = this.response;
$("#video-id").attr("src", URL.createObjectURL(blob));
}
};
xhr.send();
do this, when video get into next loop, the brower will get a 206 status.
I'm working on an app who contains a page of videos.
The front is in Angular and the back in Node.js
I choice to store my videos directly with API in the assets folder.
files.forEach(file => {
console.log(file);
});
});
I can take my videos's path with fs.
At this moment i can only res one video with this code :
const path = 'videos/Cycle de vie des déchets/test.mp4'
const stat = fs.statSync(path)
const fileSize = stat.size
const range = req.headers.range
if (range) {
const parts = range.replace(/bytes=/, "").split("-")
const start = parseInt(parts[0], 10)
const end = parts[1]
? parseInt(parts[1], 10)
: fileSize-1
if(start >= fileSize) {
res.status(416).send('Requested range not satisfiable\n'+start+' >= '+fileSize);
return
}
const chunksize = (end-start)+1
const file = fs.createReadStream(path, {start, end})
const head = {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4',
}
res.writeHead(206, head)
file.pipe(res)
} else {
const head = {
'Content-Length': fileSize,
'Content-Type': 'video/mp4',
}
res.writeHead(200, head)
fs.createReadStream(path).pipe(res)
}
And my template in angular is similar to this:
<video height="100%" width="100%" controls (click)="toggleVideo()" #videoPlayer>
<source src="http://localhost:4000/andromede/videos" type="video/mp4" />
Browser not supported
</video>
As you can see, the front request directly the API.
So, my question is : How i can res many videos with fs and my method to send videos to the client ?
Thank You
I will answer my own question.
I managed to solve the problem.
First of all, I create a query that retrieves the name of the video.
Then another query that takes the file name as a parameter.
Here is my html :
src="http://localhost:4000/andromede/videos/getVideo?videoName={{files}}"
Here is my 2nd controller for my 2nd request:
const folder = req.query.folder
const videoName = req.query.videoName
const range = req.headers.range;
if (!range){
res.status(400).send("Requires Range header");
}
const videoPath = "./videos/" + folder + "/" + videoName;
const videoSize = fs.statSync(videoPath).size;
const CHUNK_SIZE= 10**6; //1MB
const start = Number(range.replace(/\D/g,""));
const end = Math.min(start + CHUNK_SIZE, videoSize - 1);
const contentLength = end - start + 1;
const headers = {
"Content-Range": `bytes ${start}-${end}/${videoSize}`,
"Accept-Ranges": "bytes",
"Content-Length": contentLength,
"Content-Type": "video/mp4",
};
res.writeHead(206,headers);
const videoStream = fs.createReadStream(videoPath, { start, end });
videoStream.pipe(res);
I made some code to play video with buffer array, but when I submit the file, the browser just showed a empty video player like below.
here is my code
--view
block content
h1= title
p Welcome to #{title}
form.upload(method='POST' enctype='multipart/form-data' action='')
input#video(type='file' name='video')
button#uploadButton(type='submit') Upload
--controller
exports.uploadVideo_post = function(req, res, next) {
var fileName = req.file.buffer;
console.log(req.headers);
const size = req.file.size
const range = req.headers.range;
if (range) {
let [start, end] = range.replace(/bytes=/, '').split('-');
start = parseInt(start, 10);
end = end ? parseInt(end, 10) : size - 1;
res.writeHead(206, {
'Content-Range': `bytes ${start}-${end}/${size}`,
'Accept-Ranges': 'bytes',
'Content-Length': (end-start) + 1,
'Content-Type': 'video/mp4'
})
streamifier.createReadStream(fileName, { start, end }).pipe(res);
} else {
res.writeHead(200, {
'Content-Length': size,
'Content-Type': 'video/mp4'
});
(streamifier.createReadStream(fileName)).pipe(res);
}
}
I used multer to get the uploaded file, and the 'fileName' is buffer array.
Can anybody help me to solve this problem?
In addition to that, it would be great if you can let me know how to re-design this player with the 'video' attribute on html.
I need to download or process a file from a soap based web service in node.js.
can someone suggest me on how to handle this in node.js
I tried with 'node-soap' or 'soap' NPM module. it worked for normal soap web service. But, not for binary steam or MTOM based SOAP web service
I want to try to answer this... It's quite interesting that 2 years and 2 months later I can not figure it out how to easily solve the same problem.
I'm trying to get the attachment from a response like:
...
headers: { 'cache-control': 'no-cache="set-cookie"',
'content-type': 'multipart/related;boundary="----=_Part_61_425861994.1525782562904";type="application/xop+xml";start="";start-info="text/xml"',
...
body: '------=_Part_61_425861994.1525782562904\r\nContent-Type:
application/xop+xml; charset=utf-8;
type="text/xml"\r\nContent-Transfer-Encoding: 8bit\r\nContent-ID:
\r\n\r\n....\r\n------=_Part_61_425861994.1525782562904\r\nContent-Type:
application/octet-stream\r\nContent-Transfer-Encoding:
binary\r\nContent-ID:
\r\n\r\n�PNG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0002,\u0000\u0000\u0005�\b\u0006\u0........binary....
I tried ws.js but no solution.
My solution:
var request = require("request");
var bsplit = require('buffer-split')
// it will extract "----=_Part_61_425861994.1525782562904" from the response
function getBoundaryFromResponse(response) {
var contentType = response.headers['content-type']
if (contentType && contentType.indexOf('boundary=') != -1 ) {
return contentType.split(';')[1].replace('boundary=','').slice(1, -1)
}
return null
}
function splitBufferWithPattern(binaryData, boundary) {
var b = new Buffer(binaryData),
delim = new Buffer(boundary),
result = bsplit(b, delim);
return result
}
var options = {
method: 'POST',
url: 'http://bla.blabal.../file',
gzip: true,
headers: {
SOAPAction: 'downloadFile',
'Content-Type': 'text/xml;charset=UTF-8'
},
body: '<soapenv: ... xml request of the file ... elope>'
};
var data = [];
var buffer = null;
var filename = "test.png"
request(options, function (error, response, body) {
if (error) throw new Error(error);
if (filename && buffer) {
console.log("filename: " + filename)
console.log(buffer.toString('base64'))
// after this, we can save the file from base64 ...
}
})
.on('data', function (chunk) {
data.push(chunk)
})
.on('end', function () {
var onlyPayload = splitBufferWithPattern(Buffer.concat(data), '\r\n\r\n') // this will get from PNG
buffer = onlyPayload[2]
buffer = splitBufferWithPattern(buffer, '\r\n-')[0]
console.log('Downloaded.');
})
I am not sure it will works in most of the cases. It looks like unstable code to my eyes and so I'm looking for something better.
Use ws.js
Here is how to fetch the file attachments:
const ws = require('ws.js')
const { Http, Mtom } = ws
var handlers = [ new Mtom(), new Http()];
var request = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' +
'<s:Body>' +
'<EchoFiles xmlns="http://tempuri.org/">' +
'<File1 />' +
'</EchoFiles>' +
'</s:Body>' +
'</s:Envelope>'
var ctx = { request: request
, contentType: "application/soap+xml"
, url: "http://localhost:7171/Service/mtom"
, action: "http://tempuri.org/IService/EchoFiles"
}
ws.send(handlers, ctx, function(ctx) {
//read an attachment from the soap response
var file = ws.getAttachment(ctx, "response", "//*[local-name(.)='File1']")
// work with the file
fs.writeFileSync("result.jpg", file)
})
Two limitations:
No basic auth provided out-of-box, patch required https://github.com/yaronn/ws.js/pull/40
If the file name is an url, you need to apply another patch at mtom.js. Replace:
.
xpath = "//*[#href='cid:" + encodeURIComponent(id) + "']//parent::*"
with:
xpath = "//*[#href='cid:" + id + "']//parent::*"