WinJS, display image from a byte array? - javascript

I'm looking for a way to display a JPEG image stored in a byte array. This is a Windows 8 Store App built in Javascript. The byte array is returned from a C# WinRT component. Its type in C# is byte[].
Basically, what I want is to get an object so that I can call:
URL.createObjectURL(bytearray, {oneTimeOnly: true});
upon. Currently this generates a runtime error because the array interface is not supported.
Thanks in advance.

I discovered a far easier method.
var blob = new Blob([bytes], {type: "image/jpg"});
var url = URL.createObjectURL(blob, { oneTimeOnly: true });
Blob is actually supported directly by URL.createObjectURL. The only catch is that you have to specify a mime format to identify the buffer format, which in my case is possible.

Current solution I found is quite round about. Hence, before giving the solution for bytes to URL, few comments:
If there is better way to get to DOM stream/blob object from bytes, try out.
If you control the winrt component - check if you can return StorageFile object. In that case - code will simplifyto
var file = MSApp.createFileFromStorageFile(storageFile);
var url = URL.createObjectURL(file, { oneTimeOnly: true });
now solution:
var bytes;
var memoryStream;
var streams = Windows.Storage.Streams;
{
// get IBuffer out of bytes
var dataWriter = new streams.DataWriter();
dataWriter.writeBytes(bytes);
var buffer = dataWriter.detachBuffer();
dataWriter.close();
// get IInputStream out of IBuffer
memoryStream = new streams.InMemoryRandomAccessStream();
return memoryStream.writeAsync(buffer);
}).then(function onmemorystreamwritecomplete()
{
var inputStream = memoryStream.getInputStreamAt(0);
// get DOM MSStream from IInputStream
var domMStream = MSApp.createStreamFromInputStream('image/jpg', inputStream);
var url = URL.createObjectURL(domMStream, { oneTimeOnly: true });
memoryStream.close();
test3Img.setAttribute('src', url);
})

Related

Creating a new Image object from a memory stream is not working in production, but works locally

The Goal: I am currently taking a file from javascript, converting it into a base64 encoded string of a byte array and passing it to my .net controller.
I am then processing that by converting that back into a byte array, creating a new memory stream of it, creating an Image object from the memory stream, changing what I need to change about it, and then bringing it back into a memory stream to then upload to my server.
The problem:
I currently am able to take an image in from JavaScript encoded like I had mentioned earlier, and upload it to the server no problem, however the issue comes in when I am trying to convert my memory stream to an Image (so that I can resize it). I am not sure why, but on my local machine it has no problems and doesn't throw any errors and uploads perfectly fine, resized and everything. However on my linux server, it gets to the log line right before it try's to create the image from the memory stream, it doesn't actually throw an exception, but just stops working and returns to my controller.
I am not really sure what is going on but hopefully some of you fine people can help :).
The code:
public async Task<DataOpResult<string>> UploadFile(StorageUploadFile file, string FileGuid)
{
Console.WriteLine("Start Upload");
DataOpResult<string> uploadFile = new DataOpResult<string>();
uploadFile.Status = DataOpResultStatus.Success;
try
{
//create storage client
StorageClient storage = await StorageClient.CreateAsync(credential);
Console.WriteLine("Created storage");
//convert file data and create Image Object
byte[] dataArray = Convert.FromBase64String(file.FileData);
Console.WriteLine("Got byte array");
Image imageVar = CreateImage(dataArray);
Console.WriteLine("Converted image");
//convert with aspect ratio
var aspectRatio = (decimal)(imageVar.Width) / (decimal)(imageVar.Height);
var newHeight = 150;
int newWidth = (int)(newHeight * aspectRatio);
//Resize Image
Console.WriteLine("Resize image");
Image resizedImage = ResizeImage(imageVar, newWidth, newHeight);
MemoryStream resizedStream = new MemoryStream();
resizedImage.Save(resizedStream, imageVar.RawFormat);
Console.WriteLine("got new memory stream");
// IUploadProgress defined in Google.Apis.Upload namespace
var progress = new Progress<IUploadProgress>(
p => Console.WriteLine($"bytes: {p.BytesSent}, status: {p.Status}")
);
// var acl = PredefinedAcl.PublicRead // public
var acl = PredefinedObjectAcl.AuthenticatedRead; // private
var options = new UploadObjectOptions { PredefinedAcl = acl };
Console.WriteLine("Upload Images");
var result = await storage.UploadObjectAsync("echochat-292801.appspot.com", $"{FileGuid}/{file.FileName}", file.FileType, resizedStream, options, progress: progress);
Console.WriteLine(result.ToString());
uploadFile.DataItem = result.MediaLink;
} catch (Exception ex)
{
uploadFile.Status = DataOpResultStatus.Error;
uploadFile.ThrownException = ex;
}
return uploadFile;
}
public static Image CreateImage(byte[] imageData)
{
Image image;
using (MemoryStream inStream = new MemoryStream())
{
inStream.Write(imageData, 0, imageData.Length);
image = Image.FromStream(inStream);
}
return image;
}
Running sudo apt-get install libgdiplus
on the my server ended up working!

How to check whether it is a blob url using javascript

I have a situation where I am converting blobURL to base64 dataURLs, but I want to do this only if url is a blobURL.
So is there any way to check whether it is valid blob url?
my blob url - blob:http://192.168.0.136/85017e84-0f2d-4791-b563-240794abdcbf
You are facing an x-y problem.
You absolutely don't need to check if your blobURI is a valid one, because you absolutely don't need to use the blobURI in order to create a base64 version of the Blob it's pointing to.
The only way to do it is to fetch the Blob and this means creating a copy of its data in memory for no-good.
What you need is a way to retrieve this Blob.
There is unfortunately no official way to do so with the web APIs, but it's not that hard to make it ourselves:
We simply have to overwrite the default URL.createObjectURL method in order to map the passed Blob in a dictionnary using the blobURI as key:
(() => {
// overrides URL methods to be able to retrieve the original blobs later on
const old_create = URL.createObjectURL;
const old_revoke = URL.revokeObjectURL;
Object.defineProperty(URL, 'createObjectURL', {
get: () => storeAndCreate
});
Object.defineProperty(URL, 'revokeObjectURL', {
get: () => forgetAndRevoke
});
Object.defineProperty(URL, 'getBlobFromObjectURL', {
get: () => getBlob
});
const dict = {};
function storeAndCreate(blob) {
var url = old_create(blob); // let it throw if it has to
dict[url] = blob;
return url
}
function forgetAndRevoke(url) {
old_revoke(url);
// some checks just because it's what the question titel asks for, and well to avoid deleting bad things
try {
if(new URL(url).protocol === 'blob:')
delete dict[url];
}catch(e){} // avoided deleting some bad thing ;)
}
function getBlob(url) {
return dict[url];
}
})();
// a few example uses
const blob = new Blob(['foo bar']);
// first normal use everyhting is alive
const url = URL.createObjectURL(blob);
const retrieved = URL.getBlobFromObjectURL(url);
console.log('retrieved: ', retrieved);
console.log('is same object: ', retrieved === blob);
// a revoked URL, of no use anymore
const revoked = URL.createObjectURL(blob);
URL.revokeObjectURL(revoked);
console.log('revoked: ', URL.getBlobFromObjectURL(revoked));
// an https:// URL
console.log('https: ', URL.getBlobFromObjectURL(location.href));
PS: for the ones concerned about the case a Blob might be closed (e.g user provided file has been deleted from disk) then simply listen for the onerror event of the FileReader you'd use in next step.
you could do something like
var url = 'blob:http://192.168.0.136/85017e84-0f2d-4791-b563-240794abdcbf';
if(url.search('blob:') == -1){
//do something
}
you may also use reg-expression based check with url.match('url expression')

Nativescript get base64 data from native image

I try to use https://github.com/bradmartin/nativescript-drawingpad for saving a signature to my backend. But I am simply not capable to find a solution to get some "useful" data from getDrawing(), which returns a native image Object, for example UIImage on iOS.
I would love to "convert" the image data to some base64 (png, or whatever) string and send it to my server.
I tried someting like:
var ImageModule = require("ui/image");
var ImageSourceModule = require("image-source");
elements.drawingpad.getDrawing().then(function(a){
var image = ImageSourceModule.fromNativeSource( a );
api.post("sign", image.toBase64String());
});
I also tried to post simply a like seen in the demo stuff.
I would really love to see a demo of how to get my hands on the "image data" itself.
thanks!
Thanks to #bradmartin I found the solution:
var image = ImageSourceModule.fromNativeSource(a);
var base64 = image.toBase64String('png');
Actually after lots of digging with tons of error, I finally figured it out.
First you have to require the nativescript-imagepicker source module and also the nativescript image source.
var imagepicker = require("nativescript-imagepicker");
var ImageSourceModule = require("tns-core-modules/image-source");
a case where you want to update a user profile and also send a base64 string to your backend for processing
function changeProfileImage(args) {
var page = args.object;
var profile = page.getViewById("profile-avatar");
var context = imagepicker.create({ mode: "single" });
context.authorize().then(function() {
return context.present();
}).then(function(selection) {
profile.background = `url(${selection[0]._android})`;
profile.backgroundRepeat = `no-repeat`;
profile.backgroundSize = `cover`;
ImageSourceModule.fromAsset(selection[0]).then(image => {
var base64 = image.toBase64String('png');
// console.log(base64);
uploadMediaFile(base64);
});
}).catch(function (e) {
// process error
console.log(e);
});
}

Calculate SHA-1 checksum of local html5 video file using JavaScript

When a video on my local storage—let's say it's currently located at file:///home/user/video.m4v—is opened by dragging it into a new tab in Chrome, how can I calculate the SHA-1 checksum for the file using JavaScript?
Purpose:
I am planning to write a Chrome extension which will store the calculated checksum of videos (files with extensions matching a pattern) as localStorage objects in order to save the playback position of video upon tab close and then restore it when the file is loaded again, even if the location or filename of the video is changed.
You need a crypto library for this. A well known one is Google CryptoJS.
I found this as an specific example for your task: https://gist.github.com/npcode/11282867
After including the crypto-js source:
function sha1sum() {
var oFile = document.getElementById('uploadFile').files[0];
var sha1 = CryptoJS.algo.SHA1.create();
var read = 0;
var unit = 1024 * 1024;
var blob;
var reader = new FileReader();
reader.readAsArrayBuffer(oFile.slice(read, read + unit));
reader.onload = function(e) {
var bytes = CryptoJS.lib.WordArray.create(e.target.result);
sha1.update(bytes);
read += unit;
if (read < oFile.size) {
blob = oFile.slice(read, read + unit);
reader.readAsArrayBuffer(blob);
} else {
var hash = sha1.finalize();
console.log(hash.toString(CryptoJS.enc.Hex)); // print the result
}
}
}
I wouldn't recommend to calculate a hash over the whole video file as it can be pretty resource consuming depending on the file size. Maybe you can use just the meta information or reconsider about the filename and filepath again?
Web APIs have progressed considerably since I asked this question. Calculating a hex digest is now possible using the built-in SubtleCrypto.digest().
TS Playground link
function u8ToHex (u8: number): string {
return u8.toString(16).padStart(2, '0');
}
/** Ref: https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#supported_algorithms */
const supportedAlgorithms = [
'SHA-1',
'SHA-256',
'SHA-384',
'SHA-512',
] as const;
type SupportedAlgorithm = typeof supportedAlgorithms[number];
type Message = string | Blob | BufferSource;
async function hexDigest (
algorithm: SupportedAlgorithm,
message: Message,
): Promise<string> {
let buf: BufferSource;
if (typeof message === 'string') buf = new TextEncoder().encode(message);
else if (message instanceof Blob) buf = await message.arrayBuffer();
else buf = message;
const hash = await crypto.subtle.digest(algorithm, buf);
return [...new Uint8Array(hash)].map(u8ToHex).join('');
}

How can I get a buffer for a file (image) from CollectionFS

I'm trying to insert an image into a pdf I'm creating server-side with PDFkit. I'm using cfs:dropbox to store my files. Before when I was using cvs:filesystem, it was easy to add the images to my pdf's cause they were right there. Now that they're stored remotely, I'm not sure how to add them, since PDFkit does not support adding images with just the url. It will, however, accept a buffer. How can I get a buffer from my CollectionFS files?
So far I have something like this:
var portrait = Portraits.findOne('vS2yFy4gxXdjTtz5d');
readStream = portrait.createReadStream('portraits');
I tried getting the buffer two ways so far:
First using dataMan, but the last command never comes back:
var dataMan = new DataMan.ReadStream(readStream, portrait.type());
var buffer = Meteor.wrapAsync(Function.prototype.bind(dataMan.getBuffer, dataMan))();
Second buffering the stream manually:
var buffer = new Buffer(0);
readStream.on('readable', function() {
buffer = Buffer.concat([buffer, readStream.read()]);
});
readStream.on('end', function() {
console.log(buffer.toString('base64'));
});
That never seems to come back either. I double-checked my doc to make sure it was there and it has a valid url and the image appears when I put the url in my browser. Am I missing something?
I had to do something similar and since there's no answer to this question, here is how I do it:
// take a cfs file and return a base64 string
var getBase64Data = function(file, callback) {
// callback has the form function (err, res) {}
var readStream = file.createReadStream();
var buffer = [];
readStream.on('data', function(chunk) {
buffer.push(chunk);
});
readStream.on('error', function(err) {
callback(err, null);
});
readStream.on('end', function() {
callback(null, buffer.concat()[0].toString('base64'));
});
};
// wrap it to make it sync
var getBase64DataSync = Meteor.wrapAsync(getBase64Data);
// get a cfs file
var file = Files.findOne();
// get the base64 string
var base64str = getBase64DataSync(file);
// get the buffer from the string
var buffer = new Buffer(base64str, 'base64')
Hope it'll help!

Categories