Filepicker.io - base64 decoded image not viewable - javascript

I'm using filepicker.io to export an existing file (FPFile) to a given destination.
I'm using the existing FPFile as sort of a small temp file, so the export happens quickly. Then, after the export is complete, I'm, trying to write back to the file I just exported with some image data (base64 encoded).
The problem is, after I've written the data, the image isn't viewable. The image doesn't display in Firefox, Chrome, or IE.
I can actually open the image in Photoshop and it displays fine, so I know the data is being written. There just seems to be sort of error in the file after it has been written to. Maybe I'm just doing something stupid.
Here's my code:
var fpfile = { url: 'https://www.filepicker.io/api/file/ermKMZgVSu2GCEouu4Lo',
filename: this.curFile.name, mimetype: 'image/jpeg', isWriteable: true};
filepicker.exportFile(
fpfile,
function(FPFile){
var data = canvas.toDataURL('image/jpeg');
writeData(FPFile, data);
}
);
function writeData(file, data){
filepicker.write(file, data,
{
base64decode: true
},
function(FPFile){
console.log("Write successful:", FPFile.filename);
},
function(FPError) {
console.log(FPError.toString());
},
function(progress) {
console.log("Loading: "+progress+"%");
}
);
}

When you use the toDataURL call, you include the prefix, for instance
data:image/png;base64
This needs to be stripped from the actual contents of the image before doing base64 decoding

Related

How do I open and display a base64 pdf from inside my Cordova App?

I am creating an App for Android using Cordova, and I would like to open and display a file (PDF or image) that is served from the server as Base64-encoded binary data.
Of course I have read the multiple other posts on the subject that already exist on this website, but none of the proposed solutions have worked for me, more details below.
To be more precise, the server sends a JSON-file to the app, which among many other things contains a string consisting of the base64-encoded contents of a PDF file. I want to convert this data back into the represented PDF and display it to the user.
If this were a pure browser page, I would simply package my base64 data into a data-URL, attach this as the href of some anchor, and add a download-attribute. Optionally I could wrap all of my data into a blob and create an object url for that first.
In Cordova, this does not work. Clicking the <a> does nothing. Here is what I have attempted so far:
Using the file plugin, I can write the binary data to a file on the device. This works, and using a terminal I can see that the file was downloaded correctly, but into an app-private directory which I cannot access normally (e.g. through the file explorer).
Accessing the user's "downloads" folder is blocked by the file system
Using window.open with the file path as the first argument and "_system" as the target does nothing. There is no error but also nothing happens. Setting the target to "_blank" instead, I get an error saying ACCESS_DENIED.
Using cordova.InAppBrowser behaves the same was as window.open
With the plugin file-opener2 installed, the app will not compile, because the plugin is looking for an android4 toolchain, and I am building for android 9 and up
The plugin document-viewer (restricting to PDFs for the time being) suffers the same problem and does not compile.
Passing the data-URI to window.open (or cordova.InAppBrowser) directly loads for a very long time and eventually tells me that the desired page could not be loaded.
The PDF file I am using for testing is roughly 17kb after converting to base64. I know this is technically above the spec for how long data-URIs can be, but Chrome in the browser has no trouble with it whatsoever, and using a much shorter URI (only a few dozen bytes) produces the same behavior.
Ideally, what I would like to do, is download the file and then trigger the user's standard browser to open the file itself. That was, I would not have to deal with MIME types and also it would look exactly how the user expected from their own device.
Alternatively, if that doesn't work, I would be ok with downloading the file into a system-wide directory and prompting the user to open it themselves. This is not optimal, but I would be able to swallow that pill.
And lastly, if there is a plugin or some other solution that solves the problem amazingly, but for PDFs only, then I can also work out something else for images (e.g. embedding a new into my app and assigning the URI to that).
I would be thankful for any suggestion you might have on how to solve this problem. The code I use to download the file currently is shown below.
Thank you for your time.
var filePath = cordova.file.externalDataDirectory; // Note: documentsDirectory is set to "" by Cordova, so I cannot use that
var fileName = "someFileName.pdf";
var mime = "application/pdf";
var dataBlob = /* some blob containing the binary data for a PDF */
function writeFile(fileEntry, dataBlob) {
// Create a FileWriter object for our FileEntry.
// This code is taken directly from the cordova-plugin-file documentation
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function() {
console.log("Successful file write...");
readFile(fileEntry);
};
fileWriter.onerror = function (e) {
console.log("Failed file write: " + e.toString());
};
fileWriter.write(dataBlob);
});
}
window.resolveLocalFileSystemURL(
filePath,
function onResolveSuccess (dirEntry) {
dirEntry.getFile(
fileName,
{ create: true },
function onGetFileSuccess (file) (
writeFile(file, dataBlob);
// At this point, the file has been downloaded successfully
window.open(file.toURL(), "_system"); // This line does nothing, and I don't understand why.
}
);
}
);
I managed to solve the problem.
As per the documentation of the file-opener2 plugin, you need to also add the androidx-adapter plugin to correct for the outdated (android 4) packages. With the plugins file, file-opener2 and androidx-adapter installed, the complete code is the following:
var filePath = cordova.file.externalDataDirectory; // Note: documentsDirectory is set to "" by Cordova, so I cannot use that
var fileName = "someFileName.pdf";
var mime = "application/pdf";
var dataBlob = /* some blob containing the binary data for a PDF */
function writeFile(fileEntry, dataBlob) {
// Create a FileWriter object for our FileEntry.
// This code is taken directly from the cordova-plugin-file documentation
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function() {
console.log("Successful file write...");
readFile(fileEntry);
};
fileWriter.onerror = function (e) {
console.log("Failed file write: " + e.toString());
};
fileWriter.write(dataBlob);
});
}
window.resolveLocalFileSystemURL(
filePath,
function onResolveSuccess (dirEntry) {
dirEntry.getFile(
fileName,
{ create: true },
function onGetFileSuccess (file) (
writeFile(file, dataBlob);
// At this point, the file has been downloaded successfully
cordova.plugins.fileOpener2.open(
    filepath + filename,
    mime,
    {
     error : function(){ },
success : function(){ }
    }
);
}
);
}
);

Using TinyMCE editor to hold image as base 64 before form submission

I'm making a Javascript app that collects the contents of a TinyMCE editor and sends it all to the server in a single post. I'm trying to allow the user to upload images into the editor, but I can't figure out how to do it without submitting the images at the time they're uploaded to a server-side image handler. I need a way to submit all images and text in one post.
I don't know a lot about handling images, but my thinking is that I can accomplish this by collecting the images as base64 strings and sending them along with the rest of the html input. First question: Is my line of thinking correct?
Second question: How do I accomplish this? I've managed to encode an uploaded image as base 64, but the TinyMCE editor image upload dialog box gets stuck open as if it's still uploading even after I already have the base 64 string.
index.html:
tinymce.init({
selector: '.texteditor',
plugins: 'autoresize code hr image link lists media searchreplace table charmap anchor help',
images_upload_url: 'scripts/image_upload.js',
images_upload_handler: function () {
console.log("heres the image_upload_handler"); //This never fires
upload_image();
},
automatic_uploads: true,
});
image_upload.js:
function upload_image(event) {
const dropzone = document.getElementsByClassName('tox-dropzone')[0];
var fileInput = dropzone.getElementsByTagName('input')[0];
var files = fileInput.files;
var file = files[0];
console.log("File " + file.name + " is " + file.size + " bytes in size");
getBase64(file);
}
function getBase64(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
console.log(reader.result); //This successfully prints the base 64 string, but the editor's dialog box won't close and the image never appears in the editor
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
When I upload an image using the editor, the result is this:
The console shows the image encoded as base 64, but the dialog box won't close. What do I need to do differently? Vanilla Javascript, please.
You misunderstand the purpose of images_upload_url - that parameter should point to a server side endpoint that processes the Base64 image and returns a new URL that is used in the <img> tag's src attribute for the image.
https://www.tiny.cloud/docs/general-configuration-guide/upload-images/
You also should not use images_upload_url and images_upload_handler - they are two different ways for TinyMCE to POST the image for processing to a server. As you have seen, if you define images_upload_url TinyMCE won't try to use images_upload_handler. You really only need images_upload_handler if our images_upload_url is not doing what you need.
The documentation page I referenced above should have the default code we execute when using images_upload_url.
I would note that you ask if you can send the Base64 images with the content when you submit the data. You can absolutely do that but the HTML payload will be significantly larger as the image binary content is now part of the HTML itself as opposed to stored externally as an image on a server (e.g. a PNG file).
Base64 images also block the browser from rendering any other HTML on the page while they process the Base64 data - this is generally considered bad for page load times.

Converting base64 data from json response into ms excel file using javascript

I've seen SO questions similar to my use case w/ angular and other server side platforms but not for pure javascript.
I have an app where I do a $.ajax and do a get call to an API, which returns a previously converted excel file (excel to base64); I need to re-convert this base64 data back into it's original form - i.e. into Excel file. I tried retracing the steps I took to convert the excel into base64, reversing some of them, but I'm not able to generate the original file. An excel file IS being generated, but it still has base64 data and therefore opens w/ errors and in a corrupted state.
Has anyone else successfully done this?
Below is my code and fiddle link: (I didn't add the base64 json data (responseData) here since it's large, but it's on the fiddle)
var bindata = window.atob(responseData);
function DownloadExcel() {
window.location.href = "data:application/vnd.ms-excel;base64, bindata"
}
var blob = new Blob([responseData], {type: 'application/vnd.ms-excel'});
if (window.navigator && window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blob);
}
else {
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
}
jsfiddle link: https://jsfiddle.net/damon_matt/2ofz6xrd/

javascript based opencv not working with dynamic image

I'm using a javascript based opencv (See: https://github.com/mtschirs/js-objectdetect)
and it works perfectly with live video using canvas and html5.
When I try to detect using a dynamically saved image it fails, but works if I hard code an image.
The following (static image):
<img id="image" src="download.png"></img>
works fine, but using
var dataURL = $("#canvas")[0].toDataURL("image/png");
$("#image").attr('src', dataURL);
or using an ajax call which saves the image onto the server and returns the url path
$.ajax({
type: "POST",
url: "saveImage.php",
data: {
img: dataURL
}
}).done(function(o) {
$("#image").attr('src', o);
});
both fail. They both display an appropriate image.
the detection function is
$("#image").objectdetect(..., function(faces) { ... }
Executes, but returns array length 0 unless I use the static image
Had one of my co-workers figure it out. Image didn't load by the time it was being computed.
jQuery.ajaxSetup({
async : false
});
I had originally tried an $(element).load(function() { .. }) didn't seem to work but it seems it was a timing issue with the ajax.

FineUploader: Thumbnails on the fly clientside

I am working on a FineUploader implementation. Special request is to create thumbnails on the fly client-side and then upload those with the original image-upload.
I have an implementation that works on FF, but does not seem to work on iOs. It looks like so:
var uploader = new qq.FineUploaderBasic({
button: document.getElementById(buttonID),
request: {
endpoint: '/up/load/a/' + $('section#ajax-viewport').data('albumid')
},
callbacks: {
onSubmit: function(id, fileName) {
// getFile obtains the file being uploaded
file = this.getFile(id);
// create a thumbnail & upload it:
ThumbDown(file, id, 200, fileName);
},
}
})
This code calls a function:
function ThumbDown(file, id, dimension, fileName) {
var reader = new FileReader();
reader.onload = function(e) {
var img = document.createElement("img");
img.onload = function (ev) {
var thumbnailDimensions; // object holding width & height of thumbnail
var c=document.getElementById("canvas-for-thumbnails"); // must be a <canvas> element
var ctx=c.getContext("2d");
// set thumbnail dimensions of canvas:
thumbnailDimensions = calcThumbnailDimension (img.width, img.height, dimension )
c.width = thumbnailDimensions.width;
c.height = thumbnailDimensions.height;
var ctx = c.getContext("2d");
ctx.drawImage(img, 0, 0, c.width, c.height);
uploadThumbnail(c.toDataURL('image/jpeg'), //a base64 encoded representation of the image
id,
fileName); // we need filename to combine with mother-image on the server
};
img.src = e.target.result;
}
reader.readAsDataURL(file);
} // end function
Finally the Thumbnail is uploaded with a dumb ajax-call:
function uploadThumbnail (base64encodedString, id, fileName) {
$.post('/up/thumb',
{
img : base64encodedString,
id: id,
fileName: fileName
},
function(data) {});
}
My questions:
1) Currently I have two uploads: one for mother-image and another for thumbnail. I would like to combine this in one FineUploader call. However, I do not see a way to do this, due to the asynchronous nature of my thumbnail creation.
Am I missing something? Is this possible to reduce this to one FineUploader call?
2) This code uploads the thumbnails as a base64 encoded string. I would like to upload the thumbnail as an image (or as a blob ?). Perhaps by following this recipe of Jeremy Banks. Would that work with FineUploader?
3) Are there other options/methods of FineUploader that I have missed but I should be using?
Any help is, as always, greatly appreciated.
So, it is already trivial to upload the original image. Fine Uploader takes care of that for you. If I understand correctly, you want to also upload a scaled version of the image (which you have already generated). I suggest you take the image you have drawn onto the canvas and convert it to a Blob. Then, you can submit this Blob directly to Fine Uploader, where it will upload it for you.
For example, change the value of uploadThumbnail to this:
function uploadThumbnail(thumbnailDataUri, id, filename) {
var imageBlob = getImageBlob(thumbnailDataUri, "image/jpeg"),
blobData = {
name: filename,
blob: imageBlob
};
// This will instruct Fine Uploader to upload the scaled image
uploader.addBlobs(blobData);
}
function getImageBlob(dataUri, type) {
var binary = atob(dataUri.split(',')[1]),
array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: type});
}
Note: the getImageBlob function was adapted from this Stack Overflow answer. If this works for you, be sure to upvote the answer I've linked to.
Server-side note
A Blob is pretty much a File without a name property. Your server-side code will handle the upload of a Blob pretty much the same way as it does a File or form submit containing a <input type="file"> form field. The only noticeable difference to your server will be the filename parameter value in the Content-Disposition header of the multipart boundary containing the file. To put it another way, your server may think the image is named "blob" or perhaps some other generic name, due to the way most browsers generate multipart encoded requests that contain Blob objects. Fine Uploader should be able to get around that by explicitly specifying a file name for the browser to include in blob's Content-Disposition header, but this ability does not have wide browser support. Fine Uploader gets around this limitation, to some degree, by including a "qqfilename" parameter with the request containing the actual name of the Blob.
Future native support for thumbnail generation & scaling
The plan is to add native support for thumbnail previews to Fine Uploader. This is covered in feature requests #868 and #896. There are other related feature requests open, such as image rotation and validation related to images. These features and other image-related features will likely be added to Fine Uploader in the very near future. Be sure to comment on the existing feature requests or add additional requests if you'd like.
As of version 4.4 of FineUploader, as Ray Nicholus pointed out would eventually happen, this functionality has been baked into their framework.
Here is an example of setting the upload sizes when creating a FineUploader instance:
var uploader = new qq.FineUploader({
...
scaling: {
sizes: [
{name: "small", maxSize: 100},
{name: "medium", maxSize: 300}
]
}
});
See their page on uploading scaled images.

Categories

Resources