How to fix 'HTML5 currentTime always sets to zero' - reactjs

I am building a videoplayer in reactjs.I have my custom sliderbar.When I click on the sliderbar,I am getting the value of the sliderbar and calculating current time of the video and setting the currenttime using document.getElementById('video').currentTime = value.
But each time when I give an input to sliderbar,the video current time is setted to 0.
But this works fine in firefox with a warning message "The Operation was aborted"
It not working in chrome.
const pos = this.seek_bar.value;
this.video = document.getElementById("video");
let new_time = (pos / 100) * this.video.duration;
if (!isNaN(new_time)) this.video.currentTime = new_time;
this.video.addEventListener("timeupdate", () => {
let value = (this.video.currentTime / this.video.duration) * 100;
if (!isNaN(value)) {
this.seek_bar.value = value;
}
});
I want the chrome to set the current time I provide.Please help me to solve this.

Got It.
The problem was with streaming the video from server.
Changed the existing code to
video(req, res) {
const videoPath = req.query.videoPath;
const stat = fs.statSync(videoPath);
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(videoPath, { 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(videoPath).pipe(res);
}
}

Related

Webassembly C program crashing at malloc

I'm working on a Javascript/web module bundler program that is written in C and a bit of JavaScript. I keep having a problem with the program where the program will randomly crash at a random malloc call in a random function. It only occurs when I use it on a more complicated project with far more files and will work fine if used on a smaller project. I've had errors with malloc and free that were being caused by memory leaks/errors in other parts of the program but as far as I can tell there is nothing wrong this time that is noticeable.
This is the error message that occurs:
RuntimeError: memory access out of bounds
at dlmalloc (wasm://wasm/007a3c2a:wasm-function[339]:0x7db59)
at ReadDataFromFile (wasm://wasm/007a3c2a:wasm-function[114]:0x4cfd8)
at BundleFile (wasm://wasm/007a3c2a:wasm-function[128]:0x55e36)
at BundleFiles (wasm://wasm/007a3c2a:wasm-function[130]:0x6b9fb)
at /mnt/c/Users/redstarbird/Documents/Projects/ArrowStack/Build/CFunctions.js:985:22
at Object.ccall (/mnt/c/Users/redstarbird/Documents/Projects/ArrowStack/Build/CFunctions.js:5328:22)
at /mnt/c/Users/redstarbird/Documents/Projects/ArrowStack/src/ArrowPack.js:104:24
The function that it crashes in looks like this:
char EMSCRIPTEN_KEEPALIVE *ReadDataFromFile(char *path)
{ // returns contents of file
printf("path: %s\n", path);
FILE *filePTR = fopen(path, "r");
if (filePTR == NULL)
{
printf("Error opening file %s\n", path);
return NULL;
}
fseek(filePTR, 0, SEEK_END); // seek to end of file
long int length = ftell(filePTR); // get length of file
fseek(filePTR, 0, SEEK_SET); // go back to start of file
char *buffer = malloc(length + 1); // This is where it crashes
if (buffer == NULL)
{
printf("Error creating buffer\n");
exit(1);
}
int currentChar = 0;
do
{
if (feof(filePTR))
{
break;
}
buffer[currentChar] = fgetc(filePTR);
currentChar++;
} while (1);
fclose(filePTR);
buffer[currentChar - 1] = '\0';
return buffer;
}
The project is compiled using this command:
emcc --no-entry -s INITIAL_MEMORY=128mb -Wl,--export=__heap_base -Wl,--export=__data_end -Wl,--export=malloc -Wl,--export=free -sENVIRONMENT=node --profiling -sRUNTIME_DEBUG=1 -fsanitize=undefined -sLLD_REPORT_UNDEFINED -g3 -sSTACK_OVERFLOW_CHECK=2 -sASSERTIONS=2 src/Main.c src/C/cJSON/cJSON.c src/DependencyGraph/DependencyGraph.c ./src/C/StringRelatedFunctions.c ./src/Regex/RegexFunctions.c ./src/DependencyGraph/FindDependencies.c ./src/SettingsSingleton/settingsSingleton.c ./src/C/ProblemHandler.c ./src/C/TextColors.c ./src/C/FileHandler.c ./src/C/IntFunctions.c ./src/Minifiers/HTMLMinifier.c ./src/C/FileTypesHandler.c ./src/C/Stack.c ./src/C/BundleFiles.c ./src/C/ProgressBar.c ./src/C/StringShiftHandler.c ./src/Minifiers/JSMinifier.c -s EXPORT_ES6=0 -s MODULARIZE -s USE_ES6_IMPORT_META=0 -s EXPORTED_RUNTIME_METHODS=["ccall"] -s NODERAWFS=1 -sBINARYEN=1 -sEXIT_RUNTIME=1 -sALLOW_MEMORY_GROWTH -o Build/CFunctions.js
The javascript code to initialise the webassembly looks like this:
#!/usr/bin/env node
"use strict";
// js wrapper for arrowpack for NPM
const fs = require("fs");
const path = require("path");
const chalk = require("chalk");
const settingsSingleton = require("./SettingsSingleton/settingsSingleton");
const DirFunctions = require("./js/DirFunctions");
const wasm_exec = require("../Build/wasm_exec.js");
const CFunctionFactory = require("../Build/CFunctions.js");
const go = new Go();
// const Sleep = require("../src/js/Sleep");
const { mkdirIfNotExists } = require("./js/DirFunctions.js");
var StartTime = performance.now();
const argv = require("yargs/yargs")(process.argv.slice(2))
.option("c", {
alias: "config-path",
describe: "Path to config file if not in working directory",
type: "string"
})
.help().argv;
var CONFIG_FILE_NAME = "ArrowPack-config.json"
if (argv.c) { CONFIG_FILE_NAME = path.join(argv.c, CONFIG_FILE_NAME) } else { console.log("no custom file thingy"); }
var rawconfigData = null;
if (fs.existsSync(CONFIG_FILE_NAME)) { rawconfigData = fs.readFileSync(CONFIG_FILE_NAME, "utf8"); }
var temp;
const Settings = new settingsSingleton(rawconfigData);
if (Settings.getValue("largeProject") === false) {
temp = DirFunctions.RecursiveWalkDir(Settings.getValue("entry")); // eventually add pluginAPI event here
} else {
let RecursiveWalkDirWASM = fs.readFileSync("../Build/RecursiveWalkDir.wasm"); const { WebAssembly } = require("wasi");
let compiledWalkDirWASM = WebAssembly.compile(wasm);
let InstanceWalkDirWASM = WebAssembly.instantiate(compiledWalkDirWASM);
const { InstanceWalkDirWASMExports } = instance;
temp = InstanceWalkDirWASMExports.walk_dir(Settings.getValue("entry"));
}
var WalkedFiles = temp.Files;
var WalkedDirs = temp.Directories;
console.log(WalkedDirs);
if (WalkedDirs) {
WalkedDirs.forEach(Dir => {
console.log(chalk.red(Dir));
var tempDir = Settings.getValue("exit") + Dir.substring(Settings.getValue("entry").length);
console.log(chalk.yellowBright(tempDir));
DirFunctions.mkdirIfNotExists(tempDir);
//DirFunctions.mkdirIfNotExists(Dir);
});
}
DirFunctions.mkdirIfNotExists("ARROWPACK_TEMP_PREPROCESS_DIR");
var AbsoluteFilesCharLength = 0;
var WrappedWalkedFiles = "";
if (WalkedFiles && WalkedFiles.length > 0) { // Paths are wrapped into one string because passing array of strings from JS to C is complicated
WalkedFiles.forEach(FilePath => { WrappedWalkedFiles += FilePath + "::"; console.log(chalk.bold.blue(FilePath)); AbsoluteFilesCharLength += FilePath.length; });
console.log(chalk.red(WalkedFiles.length));
var StructsPointer;
CFunctionFactory().then((CFunctions) => {
CFunctions._CheckWasm();
CFunctions._InitFileTypes();
for (let k in Settings.settings) {
if (CFunctions.ccall(
"SendSettingsString",
"number",
["string"],
[k]
) != 1) {
throw "Error sending Wasm settings string: " + k;
}
console.log(chalk.bold.blue(k));
// Gives time to apply settings
if (CFunctions.ccall(
"SendSettingsString",
"number",
["string"],
[Settings.settings[k].toString()]
) != 1) {
throw "Error sending Wasm settings string: " + Settings.settings[k];
}
console.log(chalk.bold.blue(Settings.settings[k]));
// Also gives time to apply settings
}
var Success;
// StructsPointer = CFunctions._CreateTree(allocateUTF8(WrappedWalkedFiles), WalkedFiles.length, AbsoluteFilesCharLength); // Need to get this working eventually for faster speed but couldn't work out allocateUTF8
StructsPointer = CFunctions.ccall(
"CreateGraph",
"number",
["string", "number"],
[WrappedWalkedFiles, WalkedFiles.length]
);
Success = CFunctions.ccall(
"BundleFiles",
"number",
["number"],
[StructsPointer]
);
if (Success === 1 || Success === 0) {
fs.rm("ARROWPACK_TEMP_PREPROCESS_DIR", { recursive: true }, (err) => {
if (err) { console.error(err); } else {
console.log("Sucessfully removed temporary preprocess directory");
}
});
DirFunctions.DeleteDirectory(); //CFunctions.ccall("PrintTimeTaken", "void", ["number", "number"], [StartTime, performance.now()]); // Not working for some reason
console.log(chalk.magentaBright("Bundling files completed in " + (performance.now() - StartTime) / 1000 + " seconds"));
}
});
// console.log("\n\nBuild completed in" + (EndTime - StartTime) / 1000 + " seconds!\n\n\n"); // Need to get Wasm code to run this because Wasm code seems to be non-blocking
/*
WebAssembly.instantiateStreaming(DependencyTreeWasmBuffer, DependencyTreeMemory).then((instance) => {
StructsPointer = instance.ccall(
"CreateTree",
"number",
["string", "number", "string"],
[WrappedWalkedFiles, WalkedFiles.length, settings.getValue("entry"), settings.getValue("exit")]
)
});
var GoWASMFileHandler;
const goWASM = fs.readFileSync("../Build/FileHandler.wasm");
WebAssembly.instantiate(goWASM, go.importObject).then(function (obj) {
GoWASMFileHandler = obj.instance;
go.run(GoWASMFileHandler);
GoWASMFileHandler.exports.HandleFiles(StructsPointer, settings.getValue("entry"));
});*/
}
The full code for the project is at https://github.com/redstarbird/arrowpack. Any help would be appreciated because I'm very stuck as to how to fix this.

Add tracks or create new Stream ending with Media Recorder ending up corrupted file

this is the last post which answer where my goal is in developing my project: RecordRTC with sending video chunks to server and record as webm or mp4 on server side. After recording the screen sharing stream, I decide to move forward to add video tracks to screen sharing. There are two ways for me to do it, by using addTracks function or create a new stream with contain the video from the screen sharing and audio from my media. However, both of them resulting me in the previous error in the aforementioned link: corrupted video.
FYI: Here is the link for anyone who wants to read more about Media Recorder: https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder
P/S: If you encourage me on switching to webRTC again, I would be appreciated if you could help me in solving the issue - the file ends up corrupted when using webRTC - in aforementioned link?
Here is the code from my backend and frontend:
Client code:
startMedia = ()=>{
this.setState({mediaState:"pending"})
navigator.mediaDevices.getDisplayMedia({video: true}).then( async (screenSharingStream)=>{
console.log(MediaRecorder.isTypeSupported('video/webm; codecs=vp8,opus'))
const socketIO = io.connect(baseURL,{query: {candidateID: this.candidateID,roundTest:this.roundTest }})
const mediaStream = await navigator.mediaDevices.getUserMedia({video:true,audio:true}).catch(e => {throw e});
const mediaReCorderoptions = {
videoBitsPerSecond : 128000,
audioBitsPerSecond:128000,
mimeType : 'video/webm; codecs=vp8,opus'
}
const [videoTrack] = screenSharingStream.getVideoTracks();
const [audioTrack] = mediaStream.getAudioTracks();
if (audioTrack && videoTrack)
videoTrack.addTrack(audioTrack)
const stream = new MediaStream([videoTrack, audioTrack]);
this.socketRef.current = socketIO;
this.mediaStream = mediaStream
this.screenSharingStream = stream
this.candidateVideoRef.current.srcObject = this.mediaStream;
this.mediaRecorder = new MediaRecorder(this.screenSharingStream,mediaReCorderoptions)
this.mediaRecorder.ondataavailable = function(event){
if (event && event.data.size>0){
const reader = new FileReader();
reader.onload = function(){
const dataURL = reader.result;
console.log('van chay')
const base64EncodedData = dataURL.split(',')[1];
//console.log(buffer)
socketIO.emit('SEND BLOB',base64EncodedData)
}
reader.readAsDataURL(event.data)
}
}
this.mediaRecorder.start(1000)
this.setState({mediaState:this.mediaRecorder.state})
}).catch(err=>{
console.log(err.name)
switch(err.name){
case 'NotAllowedError':
message.error('Candidate does not allow!!')
this.setState({mediaState:"Aborting"})
break;
default:
message.error('System Error. Please contact us!')
this.setState({mediaState:"Aborting"})
break;
}
})
}
stopMedia = () =>{
if (this.mediaStream){
this.mediaStream.getTracks().forEach((track)=>{
if (track.readyState==='live') {
track.stop()
this.candidateVideoRef.current.style.display='none';
}})
}
if (this.screenSharingStream) {
this.mediaRecorder.stop()
this.setState({mediaState:this.mediaRecorder.state})
}
}
Server code:
socket.on("SEND BLOB",chunk=>{
try {
//if (chunk instanceof Buffer){
const fileExtension = '.webm'
const dataBuffer = new Buffer(chunk, 'base64');
const fileStream = fs.createWriteStream(path.join(__dirname,'./videos/candidate/',candidateID + '-' + roundTest + fileExtension), {flags: 'a'});
fileStream.write(dataBuffer);
}
catch(e){
console.log(e)
}
})

Using a separate AudioContext / scriptProcessor Node in Wavesurfer

I'm attempting to use a separate context/script processor from Wavesurfer's default so I can manipulate the pitch of the audio independent of playback rate. When I attempt to give the context/script processor as parameters, and playback the audio, I don't get any sound.
My Waveform component:
const playbackEngine = new PlaybackEngine({
emitter: emitter,
pitch: pitch,
});
const Waveform = WaveSurfer.create({
audioContext: playbackEngine.context,
audioScriptProcessor: playbackEngine.scriptProcessor,
barWidth: 1,
cursorWidth: 1,
pixelRatio: 1,
container: '#audio-spectrum',
progressColor: '#03a9f4',
height: 100,
normalize: true,
responsive: true,
waveColor: '#ccc',
cursorColor: '#4a74a5'
});
// called in ComponentDidMount()
function loadMediaUrl(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = async function() {
let buffer = request.response;
// sets arrayBuffer for Playback Engine
const audioBuff = await playbackEngine.decodeAudioData(buffer, (error) => {
console.error(`Error decoding audio:`, error);
});
// sets audioBuffer for Wavesurfer to render Waveform (where I believe the problem
// begins)
Waveform.loadDecodedBuffer(audioBuff);
// sets audioBuffer for Playback Engine to playback audio
playbackEngine.setBuffer(audioBuff);
}
request.send();
}
Playback.js
const {SimpleFilter, SoundTouch} = require('./soundtouch');
const BUFFER_SIZE = 4096;
class PlaybackEngine {
constructor({emitter, pitch}) {
this.emitter = emitter;
this.context = new (window.AudioContext || window.webkitAudioContext);
this.scriptProcessor = this.context.createScriptProcessor(BUFFER_SIZE, 2, 2);
this.scriptProcessor.onaudioprocess = e => {
const l = e.outputBuffer.getChannelData(0);
const r = e.outputBuffer.getChannelData(1);
const framesExtracted = this.simpleFilter.extract(this.samples, BUFFER_SIZE);
if (framesExtracted === 0) {
this.emitter.emit('stop');
}
for (let i = 0; i < framesExtracted; i++) {
l[i] = this.samples[i * 2];
r[i] = this.samples[i * 2 + 1];
}
};
this.soundTouch = new SoundTouch();
this.soundTouch.pitch = pitch;
this.duration = undefined;
}
get pitch() {
return this.soundTouch.pitch;
}
set pitch(pitch) {
this.soundTouch.pitch = pitch;
}
decodeAudioData(data) {
return this.context.decodeAudioData(data);
}
setBuffer(buffer) {
const bufferSource = this.context.createBufferSource();
bufferSource.buffer = buffer;
this.samples = new Float32Array(BUFFER_SIZE * 2);
this.source = {
extract: (target, numFrames, position) => {
this.emitter.emit('time', (position / this.context.sampleRate));
const l = buffer.getChannelData(0);
const r = buffer.getChannelData(1);
for (let i = 0; i < numFrames; i++) {
target[i * 2] = l[i + position];
target[i * 2 + 1] = r[i + position];
}
return Math.min(numFrames, l.length - position);
},
};
this.simpleFilter = new SimpleFilter(this.source, this.soundTouch);
this.duration = buffer.duration;
this.emitter.emit('duration', buffer.duration);
}
play() {
this.scriptProcessor.connect(this.context.destination);
}
pause() {
this.scriptProcessor.disconnect(this.context.destination);
}
seekPercent(percent) {
if (this.simpleFilter !== undefined) {
this.simpleFilter.sourcePosition = Math.round(
percent / 100 * this.duration * this.context.sampleRate
);
}
}
}
export default PlaybackEngine;
In this setup, with Waveform.play() I can cause playback from the wavesurfer instance but cannot manipulate the pitch. Similarly, with playbackEngine.play() I can manipulate the pitch but lose all Wavesurfer functionality.
Though I'm pretty sure the problem stems from Wavesurfer and my Playback Engine using two separate AudioBuffers, I need to set up the buffer in my playback context, as well as render the waveform with wavesurfer.
I'd like to see if anyone can confirm how to use the Playback Engine's context, script processor, and AudioBuffer to control the Wavesurfer instance (ie. having Waveform.play() play audio from the Playback Engine, as well as update the Wavesurfer UI).
All help is appreciated.
So I ended up manually removing
audioScriptProcessor: playbackEngine.scriptProcessor,
from the Wavesurfer initialization, then attaching playbackEngine's script processor to the destinationNode manually. I had a previously attempt set up like this, and heard annoying popping sounds during playback. What I thought was annoying sample/buffer errors was actually coming from an EventEmitter instance I had constantly broadcasting time between the files. Removing that solved my noise issue (ツ)

Camera streaming service works only with localhost but not with IP address

I have implemented a service which streams camera output on html5. But it works only if I use localhost:8080 localhost if I use IP address or machine name then it does not even detect the camera.
/*global logger*/
/*
VisualInspection
========================
#file : VisualInspection.js
#version : 1.0.0
#author :
#date : 7/28/2019
#copyright :
#license : Apache 2
Documentation
========================
Describe your widget here.
*/
// Required module list. Remove unnecessary modules, you can always get them back from the boilerplate.
define([
"dojo/_base/declare",
"mxui/widget/_WidgetBase",
"dijit/_TemplatedMixin",
"mxui/dom",
"dojo/dom",
"dojo/dom-prop",
"dojo/dom-geometry",
"dojo/dom-class",
"dojo/dom-style",
"dojo/dom-construct",
"dojo/_base/array",
"dojo/_base/lang",
"dojo/text",
"dojo/html",
"dojo/_base/event",
"VisualInspection/lib/jquery-1.11.2",
"dojo/text!VisualInspection/widget/template/VisualInspection.html",
"VisualInspection/widget/template/tf.min",
// "dojo/text!VisualInspection/widget/template/labels.json",
// "dojo/text!VisualInspection/widget/template/model.json"
], function (declare, _WidgetBase, _TemplatedMixin, dom, dojoDom, dojoProp, dojoGeometry, dojoClass, dojoStyle, dojoConstruct, dojoArray, lang, dojoText, dojoHtml, dojoEvent, _jQuery, widgetTemplate, tf) {
"use strict";
var $ = _jQuery.noConflict(true);
var LABELS_URL = "http://pni6w2465:7777/EasyPlan/model_web/labels.json"
var MODEL_JSON = "http://pni6w2465:7777/EasyPlan/model_web/model.json"
// var tf = require(['../../VisualInspection/node_modules/#tensorflow/tfjs']);
//////////////
const TFWrapper = model => {
const calculateMaxScores = (scores, numBoxes, numClasses) => {
const maxes = []
const classes = []
for (let i = 0; i < numBoxes; i++) {
let max = Number.MIN_VALUE
let index = -1
for (let j = 0; j < numClasses; j++) {
if (scores[i * numClasses + j] > max) {
max = scores[i * numClasses + j]
index = j
}
}
maxes[i] = max
classes[i] = index
}
return [maxes, classes]
}
const buildDetectedObjects = (
width,
height,
boxes,
scores,
indexes,
classes
) => {
const count = indexes.length
const objects = []
for (let i = 0; i < count; i++) {
const bbox = []
for (let j = 0; j < 4; j++) {
bbox[j] = boxes[indexes[i] * 4 + j]
}
const minY = bbox[0] * height
const minX = bbox[1] * width
const maxY = bbox[2] * height
const maxX = bbox[3] * width
bbox[0] = minX
bbox[1] = minY
bbox[2] = maxX - minX
bbox[3] = maxY - minY
objects.push({
bbox: bbox,
class: classes[indexes[i]],
score: scores[indexes[i]]
})
}
return objects
}
var img = null;
const detect = input => {
const batched = tf.tidy(() => {
const img = tf.browser.fromPixels(input)
// Reshape to a single-element batch so we can pass it to executeAsync.
// var img = null;
// //sid
// var canvas = document.querySelector("#canvasElement");
// if (canvas.getContext) {
// var ctx = canvas.getContext("2d");
// img = canvas.toDataURL("image/png");
// }
return img.expandDims(0)
})
const height = batched.shape[1]
const width = batched.shape[2]
// const height = img.height
// const width = img.width
return model.executeAsync(batched).then(result => {
const scores = result[0].dataSync()
const boxes = result[1].dataSync()
// clean the webgl tensors
batched.dispose()
tf.dispose(result)
const [maxScores, classes] = calculateMaxScores(
scores,
result[0].shape[1],
result[0].shape[2]
)
const prevBackend = tf.getBackend()
// run post process in cpu
tf.setBackend('cpu')
const indexTensor = tf.tidy(() => {
const boxes2 = tf.tensor2d(boxes, [
result[1].shape[1],
result[1].shape[3]
])
return tf.image.nonMaxSuppression(
boxes2,
maxScores,
20, // maxNumBoxes
0.5, // iou_threshold
0.5 // score_threshold
)
})
const indexes = indexTensor.dataSync()
indexTensor.dispose()
// restore previous backend
tf.setBackend(prevBackend)
return buildDetectedObjects(
width,
height,
boxes,
maxScores,
indexes,
classes
)
})
}
return {
detect: detect
}
}
//////////////////////
// Declare widget's prototype.
return declare("VisualInspection.widget.VisualInspection", [_WidgetBase, _TemplatedMixin], {
// _TemplatedMixin will create our dom node using this HTML template.
templateString: widgetTemplate,
// DOM elements
inputNodes: null,
colorSelectNode: null,
colorInputNode: null,
infoTextNode: null,
// Parameters configured in the Modeler.
mfToExecute: "",
messageString: "",
backgroundColor: "",
// Internal variables. Non-primitives created in the prototype are shared between all widget instances.
_handles: null,
_contextObj: null,
_alertDiv: null,
_readOnly: false,
// dojo.declare.constructor is called to construct the widget instance. Implement to initialize non-primitive properties.
constructor: function () {
logger.debug(this.id + ".constructor");
this._handles = [];
},
// dijit._WidgetBase.postCreate is called after constructing the widget. Implement to do extra setup work.
postCreate: function () {
logger.debug(this.id + ".postCreate");
if (this.readOnly || this.get("disabled") || this.readonly) {
this._readOnly = true;
}
this._updateRendering();
this._setupEvents();
var video = document.querySelector("#videoElement");
var canvas = document.querySelector("#canvasElement");
// if (navigator.mediaDevices.getUserMedia) {
// navigator.mediaDevices.getUserMedia({ video: true })
// .then(function (stream) {
// video.srcObject = stream;
// })
// .catch(function (err0r) {
// console.log("Something went wrong!");
// });
// }
this.componentDidMount();
},
////////////////////////////////////////////////////////
componentDidMount: function () {
var video = document.querySelector("#videoElement");
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
const webCamPromise = navigator.mediaDevices
.getUserMedia({
audio: false,
video: {
facingMode: 'user'
}
})
.then(stream => {
window.stream = stream
video.srcObject = stream
return new Promise((resolve, _) => {
video.onloadedmetadata = () => {
resolve()
}
})
})
const modelPromise = tf.loadGraphModel(MODEL_JSON)
const labelsPromise = fetch(LABELS_URL).then(data => data.json())
Promise.all([modelPromise, labelsPromise, webCamPromise])
.then(values => {
const [model, labels] = values
this.detectFrame(video, model, labels)
})
.catch(error => {
console.error(error)
})
}
},
detectFrame: function (video, model, labels) {
TFWrapper(model)
.detect(video)
.then(predictions => {
this.renderPredictions(predictions, labels)
requestAnimationFrame(() => {
this.detectFrame(video, model, labels)
})
})
},
renderPredictions: function (predictions, labels) {
var canvas = document.querySelector("#canvasElement");
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
// Font options.
const font = '16px sans-serif'
ctx.font = font
ctx.textBaseline = 'top'
predictions.forEach(prediction => {
const x = prediction.bbox[0]
const y = prediction.bbox[1]
const width = prediction.bbox[2]
const height = prediction.bbox[3]
const label = labels[parseInt(prediction.class)]
// Draw the bounding box.
ctx.strokeStyle = '#00FFFF'
ctx.lineWidth = 4
ctx.strokeRect(x, y, width, height)
// Draw the label background.
ctx.fillStyle = '#00FFFF'
const textWidth = ctx.measureText(label).width
const textHeight = parseInt(font, 10) // base 10
ctx.fillRect(x, y, textWidth + 4, textHeight + 4)
})
predictions.forEach(prediction => {
const x = prediction.bbox[0]
const y = prediction.bbox[1]
const label = labels[parseInt(prediction.class)]
// Draw the text last to ensure it's on top.
ctx.fillStyle = '#000000'
ctx.fillText(label, x, y)
})
},
///////////////////////////////////////////////////////////
// mxui.widget._WidgetBase.update is called when context is changed or initialized. Implement to re-render and / or fetch data.
update: function (obj, callback) {
logger.debug(this.id + ".update");
this._contextObj = obj;
this._resetSubscriptions();
this._updateRendering(callback); // We're passing the callback to updateRendering to be called after DOM-manipulation
},
// mxui.widget._WidgetBase.enable is called when the widget should enable editing. Implement to enable editing if widget is input widget.
enable: function () {
logger.debug(this.id + ".enable");
},
// mxui.widget._WidgetBase.enable is called when the widget should disable editing. Implement to disable editing if widget is input widget.
disable: function () {
logger.debug(this.id + ".disable");
},
// mxui.widget._WidgetBase.resize is called when the page's layout is recalculated. Implement to do sizing calculations. Prefer using CSS instead.
resize: function (box) {
logger.debug(this.id + ".resize");
},
// mxui.widget._WidgetBase.uninitialize is called when the widget is destroyed. Implement to do special tear-down work.
uninitialize: function () {
logger.debug(this.id + ".uninitialize");
// Clean up listeners, helper objects, etc. There is no need to remove listeners added with this.connect / this.subscribe / this.own.
},
// We want to stop events on a mobile device
_stopBubblingEventOnMobile: function (e) {
logger.debug(this.id + "._stopBubblingEventOnMobile");
if (typeof document.ontouchstart !== "undefined") {
dojoEvent.stop(e);
}
},
// Attach events to HTML dom elements
_setupEvents: function () {
logger.debug(this.id + "._setupEvents");
this.connect(this.colorSelectNode, "change", function (e) {
// Function from mendix object to set an attribute.
this._contextObj.set(this.backgroundColor, this.colorSelectNode.value);
});
this.connect(this.infoTextNode, "click", function (e) {
// Only on mobile stop event bubbling!
this._stopBubblingEventOnMobile(e);
// If a microflow has been set execute the microflow on a click.
if (this.mfToExecute !== "") {
this._execMf(this.mfToExecute, this._contextObj.getGuid());
}
});
},
_execMf: function (mf, guid, cb) {
logger.debug(this.id + "._execMf");
if (mf && guid) {
mx.ui.action(mf, {
params: {
applyto: "selection",
guids: [guid]
},
callback: lang.hitch(this, function (objs) {
if (cb && typeof cb === "function") {
cb(objs);
}
}),
error: function (error) {
console.debug(error.description);
}
}, this);
}
},
// Rerender the interface.
_updateRendering: function (callback) {
logger.debug(this.id + "._updateRendering");
// Important to clear all validations!
this._clearValidations();
// The callback, coming from update, needs to be executed, to let the page know it finished rendering
this._executeCallback(callback, "_updateRendering");
},
// Handle validations.
_handleValidation: function (validations) {
logger.debug(this.id + "._handleValidation");
this._clearValidations();
var validation = validations[0],
message = validation.getReasonByAttribute(this.backgroundColor);
if (this._readOnly) {
validation.removeAttribute(this.backgroundColor);
} else if (message) {
this._addValidation(message);
validation.removeAttribute(this.backgroundColor);
}
},
// Clear validations.
_clearValidations: function () {
logger.debug(this.id + "._clearValidations");
dojoConstruct.destroy(this._alertDiv);
this._alertDiv = null;
},
// Show an error message.
_showError: function (message) {
logger.debug(this.id + "._showError");
if (this._alertDiv !== null) {
dojoHtml.set(this._alertDiv, message);
return true;
}
this._alertDiv = dojoConstruct.create("div", {
"class": "alert alert-danger",
"innerHTML": message
});
dojoConstruct.place(this._alertDiv, this.domNode);
},
// Add a validation.
_addValidation: function (message) {
logger.debug(this.id + "._addValidation");
this._showError(message);
},
// Reset subscriptions.
_resetSubscriptions: function () {
logger.debug(this.id + "._resetSubscriptions");
// Release handles on previous object, if any.
this.unsubscribeAll();
// When a mendix object exists create subscribtions.
if (this._contextObj) {
this.subscribe({
guid: this._contextObj.getGuid(),
callback: lang.hitch(this, function (guid) {
this._updateRendering();
})
});
this.subscribe({
guid: this._contextObj.getGuid(),
attr: this.backgroundColor,
callback: lang.hitch(this, function (guid, attr, attrValue) {
this._updateRendering();
})
});
this.subscribe({
guid: this._contextObj.getGuid(),
val: true,
callback: lang.hitch(this, this._handleValidation)
});
}
},
_executeCallback: function (cb, from) {
logger.debug(this.id + "._executeCallback" + (from ? " from " + from : ""));
if (cb && typeof cb === "function") {
cb();
}
}
});
});
require(["VisualInspection/widget/VisualInspection"]);
<div id="container">
<video autoplay="true" playsInline="true" width="600" height="500" id="videoElement" style="position: fixed;" >
</video>
<canvas id= "canvasElement" width="600" height="500" style="position: absolute;">
</canvas>
</div>
From Above code, When I run using localhost this code will execute :
<!-- begin snippet: js hide: false console: true babel: false -->
When I run using machine name or IP name :
This code is not considered in developer tool of chrome due to security or some reason

Can't open my Blob in PDF on chrome AngularJs [duplicate]

I have Base64-encoded binary data in a string:
const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
I would like to create a blob: URL containing this data and display it to the user:
const blob = new Blob(????, {type: contentType});
const blobUrl = URL.createObjectURL(blob);
window.location = blobUrl;
I haven't been been able to figure out how to create the BLOB.
In some cases I am able to avoid this by using a data: URL instead:
const dataUrl = `data:${contentType};base64,${b64Data}`;
window.location = dataUrl;
However, in most cases the data: URLs are prohibitively large.
How can I decode a Base64 string to a BLOB object in JavaScript?
The atob function will decode a Base64-encoded string into a new string with a character for each byte of the binary data.
const byteCharacters = atob(b64Data);
Each character's code point (charCode) will be the value of the byte. We can create an array of byte values by applying this using the .charCodeAt method for each character in the string.
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
You can convert this array of byte values into a real typed byte array by passing it to the Uint8Array constructor.
const byteArray = new Uint8Array(byteNumbers);
This in turn can be converted to a BLOB by wrapping it in an array and passing it to the Blob constructor.
const blob = new Blob([byteArray], {type: contentType});
The code above works. However the performance can be improved a little by processing the byteCharacters in smaller slices, rather than all at once. In my rough testing 512 bytes seems to be a good slice size. This gives us the following function.
const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, {type: contentType});
return blob;
}
const blob = b64toBlob(b64Data, contentType);
const blobUrl = URL.createObjectURL(blob);
window.location = blobUrl;
Full Example:
const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, {type: contentType});
return blob;
}
const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
const blob = b64toBlob(b64Data, contentType);
const blobUrl = URL.createObjectURL(blob);
const img = document.createElement('img');
img.src = blobUrl;
document.body.appendChild(img);
Here is a more minimal method without any dependencies or libraries.
It requires the new fetch API. (Can I use it?)
var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
fetch(url)
.then(res => res.blob())
.then(console.log)
With this method you can also easily get a ReadableStream, ArrayBuffer, text, and JSON.
(fyi this also works with node-fetch in Node)
As a function:
const b64toBlob = (base64, type = 'application/octet-stream') =>
fetch(`data:${type};base64,${base64}`).then(res => res.blob())
But I would encourage you to don't use base64 in the first place. There are better ways to send and receive binary data. JSON isn't always the best option. it takes up more bandwidth and waste processing time (de)encodeing stuff. Us eg canvas.toBlob instead of canvas.toDataURL and use FormData to send binary files. you can also return back a multipart payload and decode it using await response.formData() that is coming from a server response. FormData can go both ways.
I did a simple performance test towards Jeremy's ES6 sync version.
The sync version will block UI for a while.
keeping the devtool open can slow the fetch performance
document.body.innerHTML += '<input autofocus placeholder="try writing">'
// get some dummy gradient image
var img=function(){var a=document.createElement("canvas"),b=a.getContext("2d"),c=b.createLinearGradient(0,0,1500,1500);a.width=a.height=3000;c.addColorStop(0,"red");c.addColorStop(1,"blue");b.fillStyle=c;b.fillRect(0,0,a.width,a.height);return a.toDataURL()}();
async function perf() {
const blob = await fetch(img).then(res => res.blob())
// turn it to a dataURI
const url = img
const b64Data = url.split(',')[1]
// Jeremy Banks solution
const b64toBlob = (b64Data, contentType = '', sliceSize=512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, {type: contentType});
return blob;
}
// bench blocking method
let i = 500
console.time('blocking b64')
while (i--) {
await b64toBlob(b64Data)
}
console.timeEnd('blocking b64')
// bench non blocking
i = 500
// so that the function is not reconstructed each time
const toBlob = res => res.blob()
console.time('fetch')
while (i--) {
await fetch(url).then(toBlob)
}
console.timeEnd('fetch')
console.log('done')
}
perf()
Optimized (but less readable) implementation:
function base64toBlob(base64Data, contentType) {
contentType = contentType || '';
var sliceSize = 1024;
var byteCharacters = atob(base64Data);
var bytesLength = byteCharacters.length;
var slicesCount = Math.ceil(bytesLength / sliceSize);
var byteArrays = new Array(slicesCount);
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
var begin = sliceIndex * sliceSize;
var end = Math.min(begin + sliceSize, bytesLength);
var bytes = new Array(end - begin);
for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return new Blob(byteArrays, { type: contentType });
}
Following is my TypeScript code which can be converted easily into JavaScript and you can use
/**
* Convert BASE64 to BLOB
* #param base64Image Pass Base64 image data to convert into the BLOB
*/
private convertBase64ToBlob(base64Image: string) {
// Split into two parts
const parts = base64Image.split(';base64,');
// Hold the content type
const imageType = parts[0].split(':')[1];
// Decode Base64 string
const decodedData = window.atob(parts[1]);
// Create UNIT8ARRAY of size same as row data length
const uInt8Array = new Uint8Array(decodedData.length);
// Insert all character code into uInt8Array
for (let i = 0; i < decodedData.length; ++i) {
uInt8Array[i] = decodedData.charCodeAt(i);
}
// Return BLOB image after conversion
return new Blob([uInt8Array], { type: imageType });
}
See this example: https://jsfiddle.net/pqhdce2L/
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
var contentType = 'image/png';
var b64Data = Your Base64 encode;
var blob = b64toBlob(b64Data, contentType);
var blobUrl = URL.createObjectURL(blob);
var img = document.createElement('img');
img.src = blobUrl;
document.body.appendChild(img);
For all browser support, especially on Android, perhaps you can add this:
try{
blob = new Blob(byteArrays, {type : contentType});
}
catch(e){
// TypeError old Google Chrome and Firefox
window.BlobBuilder = window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
if(e.name == 'TypeError' && window.BlobBuilder){
var bb = new BlobBuilder();
bb.append(byteArrays);
blob = bb.getBlob(contentType);
}
else if(e.name == "InvalidStateError"){
// InvalidStateError (tested on FF13 WinXP)
blob = new Blob(byteArrays, {type : contentType});
}
else{
// We're screwed, blob constructor unsupported entirely
}
}
For all copy-paste lovers out there like me, here is a cooked download function which works on Chrome, Firefox and Edge:
window.saveFile = function (bytesBase64, mimeType, fileName) {
var fileUrl = "data:" + mimeType + ";base64," + bytesBase64;
fetch(fileUrl)
.then(response => response.blob())
.then(blob => {
var link = window.document.createElement("a");
link.href = window.URL.createObjectURL(blob, { type: mimeType });
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
}
I'm posting a more declarative way of sync Base64 converting. While async fetch().blob() is very neat and I like this solution a lot, it doesn't work on Internet Explorer 11 (and probably Edge - I haven't tested this one), even with the polyfill - take a look at my comment to Endless' post for more details.
const blobPdfFromBase64String = base64String => {
const byteArray = Uint8Array.from(
atob(base64String)
.split('')
.map(char => char.charCodeAt(0))
);
return new Blob([byteArray], { type: 'application/pdf' });
};
Bonus
If you want to print it you could do something like:
const isIE11 = !!(window.navigator && window.navigator.msSaveOrOpenBlob); // Or however you want to check it
const printPDF = blob => {
try {
isIE11
? window.navigator.msSaveOrOpenBlob(blob, 'documents.pdf')
: printJS(URL.createObjectURL(blob)); // http://printjs.crabbly.com/
} catch (e) {
throw PDFError;
}
};
Bonus x 2 - Opening a BLOB file in new tab for Internet Explorer 11
If you're able to do some preprocessing of the Base64 string on the server you could expose it under some URL and use the link in printJS :)
For image data, I find it simpler to use canvas.toBlob (asynchronous)
function b64toBlob(b64, onsuccess, onerror) {
var img = new Image();
img.onerror = onerror;
img.onload = function onload() {
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
canvas.toBlob(onsuccess);
};
img.src = b64;
}
var base64Data = 'data:image/jpg;base64,/9j/4AAQSkZJRgABAQA...';
b64toBlob(base64Data,
function(blob) {
var url = window.URL.createObjectURL(blob);
// do something with url
}, function(error) {
// handle error
});
If you can stand adding one dependency to your project there's the great blob-util npm package that provides a handy base64StringToBlob function. Once added to your package.json you can use it like this:
import { base64StringToBlob } from 'blob-util';
const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
const blob = base64StringToBlob(b64Data, contentType);
// Do whatever you need with your blob...
I noticed that Internet Explorer 11 gets incredibly slow when slicing the data like Jeremy suggested. This is true for Chrome, but Internet Explorer seems to have a problem when passing the sliced data to the Blob-Constructor. On my machine, passing 5 MB of data makes Internet Explorer crash and memory consumption is going through the roof. Chrome creates the blob in no time.
Run this code for a comparison:
var byteArrays = [],
megaBytes = 2,
byteArray = new Uint8Array(megaBytes*1024*1024),
block,
blobSlowOnIE, blobFastOnIE,
i;
for (i = 0; i < (megaBytes*1024); i++) {
block = new Uint8Array(1024);
byteArrays.push(block);
}
//debugger;
console.profile("No Slices");
blobSlowOnIE = new Blob(byteArrays, { type: 'text/plain'});
console.profileEnd();
console.profile("Slices");
blobFastOnIE = new Blob([byteArray], { type: 'text/plain'});
console.profileEnd();
So I decided to include both methods described by Jeremy in one function. Credits go to him for this.
function base64toBlob(base64Data, contentType, sliceSize) {
var byteCharacters,
byteArray,
byteNumbers,
blobData,
blob;
contentType = contentType || '';
byteCharacters = atob(base64Data);
// Get BLOB data sliced or not
blobData = sliceSize ? getBlobDataSliced() : getBlobDataAtOnce();
blob = new Blob(blobData, { type: contentType });
return blob;
/*
* Get BLOB data in one slice.
* => Fast in Internet Explorer on new Blob(...)
*/
function getBlobDataAtOnce() {
byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
byteArray = new Uint8Array(byteNumbers);
return [byteArray];
}
/*
* Get BLOB data in multiple slices.
* => Slow in Internet Explorer on new Blob(...)
*/
function getBlobDataSliced() {
var slice,
byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
slice = byteCharacters.slice(offset, offset + sliceSize);
byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
byteArray = new Uint8Array(byteNumbers);
// Add slice
byteArrays.push(byteArray);
}
return byteArrays;
}
}
The method with fetch is the best solution, but if anyone needs to use a method without fetch then here it is, as the ones mentioned previously didn't work for me:
function makeblob(dataURL) {
const BASE64_MARKER = ';base64,';
const parts = dataURL.split(BASE64_MARKER);
const contentType = parts[0].split(':')[1];
const raw = window.atob(parts[1]);
const rawLength = raw.length;
const uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], { type: contentType });
}
In browser just
Uint8Array.from(atob(YOUR_BASE64_DATA), (c) => c.charCodeAt(0))
compare with fetch
!(async () => {
const start = performance.now();
let i = 0;
while (i++ < 1e3) {
const dataUrl =
"data:application/octet-stream;base64,H4sIAAAAAAAAA0vOzyvOz0nVy8lP10jISM3JyVdIr8osUFCpdkksSdXLyy/X0KxN0ORKHlU3qm5U3ai6UXWj6kauOgBVt1KRLwcAAA==";
body = await (await fetch(dataUrl)).blob();
}
console.log(performance.now() - start); // 508.19999999925494ms
})();
!(async () => {
const start = performance.now();
let i = 0;
while (i++ < 1e3) {
const base64Data =
"H4sIAAAAAAAAA0vOzyvOz0nVy8lP10jISM3JyVdIr8osUFCpdkksSdXLyy/X0KxN0ORKHlU3qm5U3ai6UXWj6kauOgBVt1KRLwcAAA==";
body = Uint8Array.from(atob(base64Data), (c) => c.charCodeAt(0));
}
console.log(performance.now() - start); // 7.899999998509884ms
})();
Depends on your data size, choose performance one.
Two different variations
function base64ToBlob(base64, contentType='image/png', chunkLength=512) {
const byteCharsArray = Array.from(atob(base64.substr(base64.indexOf(',') + 1)));
const chunksIterator = new Array(Math.ceil(byteCharsArray.length / chunkLength));
const bytesArrays = [];
for (let c = 0; c < chunksIterator.length; c++) {
bytesArrays.push(new Uint8Array(byteCharsArray.slice(c * chunkLength, chunkLength * (c + 1)).map(s => s.charCodeAt(0))));
}
const blob = new Blob(bytesArrays, {type: contentType});
return blob;
}
/* Not sure how it performs with big images */
async function base64ToBlobLight(base64) { return await fetch(base64).then(res => res.blob()); }
/* Test */
const base64Data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAB+FBMVEUAAAA/mUPidDHiLi5Cn0XkNTPmeUrkdUg/m0Q0pEfcpSbwaVdKskg+lUP4zA/iLi3msSHkOjVAmETdJSjtYFE/lkPnRj3sWUs8kkLeqCVIq0fxvhXqUkbVmSjwa1n1yBLepyX1xxP0xRXqUkboST9KukpHpUbuvRrzrhF/ljbwaljuZFM4jELaoSdLtElJrUj1xxP6zwzfqSU4i0HYnydMtUlIqUfywxb60AxZqEXaoifgMCXptR9MtklHpEY2iUHWnSjvvRr70QujkC+pUC/90glMuEnlOjVMt0j70QriLS1LtEnnRj3qUUXfIidOjsxAhcZFo0bjNDH0xxNLr0dIrUdmntVTkMoyfL8jcLBRuErhJyrgKyb4zA/5zg3tYFBBmUTmQTnhMinruBzvvhnxwxZ/st+Ktt5zp9hqota2vtK6y9FemNBblc9HiMiTtMbFtsM6gcPV2r6dwroseLrMrbQrdLGdyKoobKbo3Zh+ynrgVllZulTsXE3rV0pIqUf42UVUo0JyjEHoS0HmsiHRGR/lmRz/1hjqnxjvpRWfwtOhusaz0LRGf7FEfbDVmqHXlJeW0pbXq5bec3fX0nTnzmuJuWvhoFFhm0FtrziBsjaAaDCYWC+uSi6jQS3FsSfLJiTirCOkuCG1KiG+wSC+GBvgyhTszQ64Z77KAAAARXRSTlMAIQRDLyUgCwsE6ebm5ubg2dLR0byXl4FDQzU1NDEuLSUgC+vr6urq6ubb29vb2tra2tG8vLu7u7uXl5eXgYGBgYGBLiUALabIAAABsElEQVQoz12S9VPjQBxHt8VaOA6HE+AOzv1wd7pJk5I2adpCC7RUcHd3d3fXf5PvLkxheD++z+yb7GSRlwD/+Hj/APQCZWxM5M+goF+RMbHK594v+tPoiN1uHxkt+xzt9+R9wnRTZZQpXQ0T5uP1IQxToyOAZiQu5HEpjeA4SWIoksRxNiGC1tRZJ4LNxgHgnU5nJZBDvuDdl8lzQRBsQ+s9PZt7s7Pz8wsL39/DkIfZ4xlB2Gqsq62ta9oxVlVrNZpihFRpGO9fzQw1ms0NDWZz07iGkJmIFH8xxkc3a/WWlubmFkv9AB2SEpDvKxbjidN2faseaNV3zoHXvv7wMODJdkOHAegweAfFPx4G67KluxzottCU9n8CUqXzcIQdXOytAHqXxomvykhEKN9EFutG22p//0rbNvHVxiJywa8yS2KDfV1dfbu31H8jF1RHiTKtWYeHxUvq3bn0pyjCRaiRU6aDO+gb3aEfEeVNsDgm8zzLy9egPa7Qt8TSJdwhjplk06HH43ZNJ3s91KKCHQ5x4sw1fRGYDZ0n1L4FKb9/BP5JLYxToheoFCVxz57PPS8UhhEpLBVeAAAAAElFTkSuQmCC';
const blob = base64ToBlob(base64Data);
const blobUrl = URL.createObjectURL(blob);
const img = document.createElement('img');
img.src = blobUrl;
document.body.appendChild(img);
/**********************/
/* Test */
(async () => {
const blob = await base64ToBlobLight(base64Data);
const blobUrl = URL.createObjectURL(blob);
const img = document.createElement('img');
img.src = blobUrl;
document.body.appendChild(img);
})();
This would prove to be much short solution.
const byteArray = new Buffer(base64String.replace(/^[\w\d;:\/]+base64\,/g, ''), 'base64');
base64String is the string containing the base 64 string.
byteArray is the array you need.
The regex replacement is optional and is just there to deal with prefix as in the case of dataurl string.

Resources