I'm maintaining an existing app (cannot change server-side code, only client-side js) and was asked to add the capability to upload files from clipboard.
At present moment there is a standard file-selection form
<form id="file_form" name="file_form" enctype="multipart/form-data" method="post" action="/upload">
<label for="selector">
File Location
</label>
<input type="file" id="selector" name="selector" title="Choose file">
<input type="submit" id="submit" value="ОК">
<input type="button" id="cancel" onclick="cancel()" value="Cancel">
</form>
So what I need is a way to fill input#selector with the file from clipboard. I don't need progress bars, preview, image crop, etc... as simple as possible, but remember that I cannot change anything on the server.
Is there solution that would work in Chrome, FF and IE?
Most things I googled were either full of excessive functionality and required a lot of external js or server-side code changes or didn't work in anything other than Chrome...
Your can refere to this site: https://www.pastefile.com
the core code paste.js such as
(function ($) {
'use strict';
var readImagesFromEditable = function (element, callback) {
setTimeout(function () {
$(element).find('img').each(function (i, img) {
getImageData(img.src, callback);
});
}, 1);
};
var getImageData = function (src, callback) {
var loader = new Image();
loader.onload = function () {
var canvas = document.createElement('canvas');
canvas.width = loader.width;
canvas.height = loader.height;
var context = canvas.getContext('2d');
context.drawImage(loader, 0, 0, canvas.width, canvas.height);
try {
var dataURL = canvas.toDataURL('image/png');
if (dataURL) {
callback({
dataURL: dataURL
});
}
} catch (err) {}
};
loader.src = src;
};
$.paste = function () {
var handler = function (e) {
var pasteBoard = $(this);
var trigger = function (event, data) {
if (arguments.length > 1)
return pasteBoard.trigger(event, data);
return function (data) {
return pasteBoard.trigger(event, data);
};
};
var clipboardData, text;
if (e.originalEvent) {
clipboardData = e.originalEvent.clipboardData;
if (clipboardData.items) {
var items = clipboardData.items;
// Copy-paste on OSX
if (items.length === 2) {
// If a user pastes image data, there are 2 items: the file name (at index 0) and the file (at index 1)
// but if the user pastes plain text or HTML, /index 0/ is the data with markup and /index 1/ is the plain, unadorned text.
if (items[0].kind === 'string' && items[1].kind === 'file' && items[1].type.match(/^image/)) {
// If the user copied a file from Finder (OS X) and pasted it in the window, this is the result. This is also the result if a user takes a screenshot and pastes it.
// Unfortunately, you can't copy & paste a file from the desktop. It just returns the file's icon image data & filename (on OS X).
} else if (items[0].kind === 'string' && items[1].kind === 'string') {
// Get the plain text
items[0].getAsString(trigger('pasteText'));
}
} else {
var item = items[0];
if (!item) return;
if (item.type.match(/^image\//)) {
trigger('pasteImage', item.getAsFile());
} else if (item.type === 'text/plain') {
item.getAsString(trigger('pasteText'));
}
}
} else {
if (clipboardData.types.length) {
text = clipboardData.getData('Text');
trigger('pasteText', text);
} else {
readImagesFromEditable(pasteBoard, trigger('pasteImage'));
}
}
} else if ((clipboardData = window.clipboardData)) {
text = clipboardData.getData('Text');
if (text) {
trigger('pasteText', text);
} else {
readImagesFromEditable(pasteBoard, trigger('pasteImage'));
}
}
setTimeout(function() {
pasteBoard.empty();
}, 1);
};
return $('<div/>')
.prop('contentEditable', true)
.css({
width: 1,
height: 1,
position: 'fixed',
left: -10000,
overflow: 'hidden'
})
.on('paste', handler);
};
})(jQuery);
it is not so hard to understand.
Related
Here is a contenteditable="true" DIV. When we paste any image in this div then it appends into document.body.appendChild with blob URL.
And then the HTML structure become like this:
<img src="blob:https://www.some.com/0606f154-ce49-4048-9321-27778280b2d3">
<img src="blob:https://www.some.com/0606f154-ce49-4048-9321-27778280b2d3">
But I want this image appended, document.body.appendChild(image);, into a div class when it gets appended.
Like this:
<div class="someclass"><img src="blob:https://www.some.com/0606f154-ce49-4048-9321-27778280b2d3"></div>
<div class="someclass"><img src="blob:https://www.some.com/0606f154-ce49-4048-9321-27778280b2d3"></div>
I tried these many things, but it didn't work:
document.body.appendChild(`<div class="someclass">` + image + '</div> );
My Whole Code is:
var PasteImage = function (el) {
this._el = el;
this._listenForPaste();
};
PasteImage.prototype._getImageFromContentEditableOnNextTick = function () {
var self = this;
// We need to wait until the next tick as Firefox will not have added the image to our
// contenteditable element
setTimeout(function () {
self._getImageFromContentEditable();
});
};
PasteImage.prototype._getURLObj = function () {
return window.URL || window.webkitURL;
};
PasteImage.prototype._pasteImage = function (image) {
this.emit('paste-image', image);
};
PasteImage.prototype._pasteImageSource = function (src) {
var self = this,
image = new Image();
image.onload = function () {
self._pasteImage(image);
};
image.src = src;
};
PasteImage.prototype._onPaste = function (e) {
// We need to check if event.clipboardData is supported (Chrome & IE)
if (e.clipboardData && e.clipboardData.items) {
// Get the items from the clipboard
var items = e.clipboardData.items;
// Loop through all items, looking for any kind of image
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
// We need to represent the image as a file
var blob = items[i].getAsFile();
// Use a URL or webkitURL (whichever is available to the browser) to create a
// temporary URL to the object
var URLObj = this._getURLObj();
var source = URLObj.createObjectURL(blob);
// The URL can then be used as the source of an image
this._pasteImageSource(source);
// Prevent the image (or URL) from being pasted into the contenteditable element
e.preventDefault();
}
}
}
else {
// If we can't handle clipboard data directly (Firefox & Safari), we need to read what was
// pasted from the contenteditable element
this._getImageFromContentEditableOnNextTick();
}
};
PasteImage.prototype._listenForPaste = function () {
var self = this;
self._origOnPaste = self._el.onpaste;
self._el.addEventListener('paste', function (e) {
self._onPaste(e);
// Preserve an existing onpaste event handler
if (self._origOnPaste) {
self._origOnPaste.apply(this, arguments);
}
});
};
// TODO: use EventEmitter instead
PasteImage.prototype.on = function (event, callback) {
this._callback = callback;
};
// TODO: use EventEmitter instead
PasteImage.prototype.emit = function (event, arg) {
this._callback(arg);
};
PasteImage.prototype._loadImage = function (src) {
return new Promise(function (resolve) {
var img = new Image();
img.onload = function () {
resolve(img);
};
img.src = src;
});
};
PasteImage.prototype._findFirstImage = function () {
var self = this;
return new Promise(function (resolve) {
for (var i in self._el.childNodes) {
var node = self._el.childNodes[i];
// Is the element an image?
if (node.tagName === 'IMG') {
resolve(node);
} else if (node.childNodes[0]) { // Are there children?
// If you copy an image from within Safari and then paste it within Safari, the image can be
// nested somewhere under the contenteditable element.
var imgs = node.getElementsByTagName('img');
if (imgs) {
resolve(imgs[0]);
}
}
}
// No image found so just resolve
resolve();
});
};
PasteImage.prototype._removeFirstImage = function () {
var self = this;
return self._findFirstImage().then(function (img) {
if (img) {
// In Safari if we copy and image and then paste an image within Safari we need to construct a
// proper image from the blob as Safari doesn't do this for us. Moreover, we need to wait for
// our converted image to be loaded before removing the image from the DOM as otherwise there
// can be a race condition where we remove the image before it has been loaded and this
// apparently stops the loading process.
return self._loadImage(img.src).then(function (loadedImage) {
img.parentElement.removeChild(img);
return loadedImage;
});
}
});
};
PasteImage.prototype._getImageFromContentEditable = function () {
var self = this;
this._removeFirstImage().then(function (img) {
// Process the pasted image
self._pasteImage(img);
});
};
// -----
var pasteImage = new PasteImage(document.getElementById('my-div'));
pasteImage.on('paste-image', function (image) {
document.body.appendChild( image );
});
<title>Paste Image Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.4.6/bluebird.min.js"></script>
<h1>
Copy an image and then press Command+V (Mac) or Ctrl+V (Windows) anywhere in the div below.
</h1>
<div id="my-div" contenteditable="true"
style="border:1px solid #777777;height: 50px;padding: 5px;"
onpaste="console.log('onpastefromhtml')">
</div>
How can I do that?
To make it clear, here is what you will need to do:
Just replace this:
var pasteImage = new PasteImage(document.getElementById('my-div'));
pasteImage.on('paste-image', function (image) {
document.body.appendChild( image );
});
With this:
var pasteImage = new PasteImage(document.getElementById('my-div'));
pasteImage.on('paste-image', function (image) {
// Create a new div element to put your image in:
var newDivElement = document.createElement("div");
// Add the image element to the div:
newDivElement.append(image);
// Append the div
document.body.append(newDivElement);
});
I have 2 function that I am trying to run, one after another. For some reason they both run at the same time, but the second one does not load properly. Is there a way to run the first function wait then run the second function?:
//run this first
$('#abc').click(function() {
$('.test1').show();
return false;
});
//run this second
(function ($) {
"use strict";
// A nice closure for our definitions
function getjQueryObject(string) {
// Make string a vaild jQuery thing
var jqObj = $("");
try {
jqObj = $(string)
.clone();
} catch (e) {
jqObj = $("<span />")
.html(string);
}
return jqObj;
}
function printFrame(frameWindow, content, options) {
// Print the selected window/iframe
var def = $.Deferred();
try {
frameWindow = frameWindow.contentWindow || frameWindow.contentDocument || frameWindow;
var wdoc = frameWindow.document || frameWindow.contentDocument || frameWindow;
if(options.doctype) {
wdoc.write(options.doctype);
}
wdoc.write(content);
wdoc.close();
var printed = false;
var callPrint = function () {
if(printed) {
return;
}
// Fix for IE : Allow it to render the iframe
frameWindow.focus();
try {
// Fix for IE11 - printng the whole page instead of the iframe content
if (!frameWindow.document.execCommand('print', false, null)) {
// document.execCommand returns false if it failed -http://stackoverflow.com/a/21336448/937891
frameWindow.print();
}
// focus body as it is losing focus in iPad and content not getting printed
$('body').focus();
} catch (e) {
frameWindow.print();
}
frameWindow.close();
printed = true;
def.resolve();
}
// Print once the frame window loads - seems to work for the new-window option but unreliable for the iframe
$(frameWindow).on("load", callPrint);
// Fallback to printing directly if the frame doesn't fire the load event for whatever reason
setTimeout(callPrint, options.timeout);
} catch (err) {
def.reject(err);
}
return def;
}
function printContentInIFrame(content, options) {
var $iframe = $(options.iframe + "");
var iframeCount = $iframe.length;
if (iframeCount === 0) {
// Create a new iFrame if none is given
$iframe = $('<iframe height="0" width="0" border="0" wmode="Opaque"/>')
.prependTo('body')
.css({
"position": "absolute",
"top": -999,
"left": -999
});
}
var frameWindow = $iframe.get(0);
return printFrame(frameWindow, content, options)
.done(function () {
// Success
setTimeout(function () {
// Wait for IE
if (iframeCount === 0) {
// Destroy the iframe if created here
$iframe.remove();
}
}, 1000);
})
.fail(function (err) {
// Use the pop-up method if iframe fails for some reason
console.error("Failed to print from iframe", err);
printContentInNewWindow(content, options);
})
.always(function () {
try {
options.deferred.resolve();
} catch (err) {
console.warn('Error notifying deferred', err);
}
});
}
function printContentInNewWindow(content, options) {
// Open a new window and print selected content
var frameWindow = window.open();
return printFrame(frameWindow, content, options)
.always(function () {
try {
options.deferred.resolve();
} catch (err) {
console.warn('Error notifying deferred', err);
}
});
}
function isNode(o) {
/* http://stackoverflow.com/a/384380/937891 */
return !!(typeof Node === "object" ? o instanceof Node : o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string");
}
$.print = $.fn.print = function () {
// Print a given set of elements
var options, $this, self = this;
// console.log("Printing", this, arguments);
if (self instanceof $) {
// Get the node if it is a jQuery object
self = self.get(0);
}
if (isNode(self)) {
// If `this` is a HTML element, i.e. for
// $(selector).print()
$this = $(self);
if (arguments.length > 0) {
options = arguments[0];
}
} else {
if (arguments.length > 0) {
// $.print(selector,options)
$this = $(arguments[0]);
if (isNode($this[0])) {
if (arguments.length > 1) {
options = arguments[1];
}
} else {
// $.print(options)
options = arguments[0];
$this = $("html");
}
} else {
// $.print()
$this = $("html");
}
}
// Default options
var defaults = {
globalStyles: true,
mediaPrint: false,
stylesheet: null,
noPrintSelector: ".no-print",
iframe: true,
append: null,
prepend: null,
manuallyCopyFormValues: true,
deferred: $.Deferred(),
timeout: 750,
title: null,
doctype: '<!doctype html>'
};
// Merge with user-options
options = $.extend({}, defaults, (options || {}));
var $styles = $("");
if (options.globalStyles) {
// Apply the stlyes from the current sheet to the printed page
$styles = $("style, link, meta, base, title");
} else if (options.mediaPrint) {
// Apply the media-print stylesheet
$styles = $("link[media=print]");
}
if (options.stylesheet) {
// Add a custom stylesheet if given
$styles = $.merge($styles, $('<link rel="stylesheet" href="' + options.stylesheet + '">'));
}
// Create a copy of the element to print
var copy = $this.clone();
// Wrap it in a span to get the HTML markup string
copy = $("<span/>")
.append(copy);
// Remove unwanted elements
copy.find(options.noPrintSelector)
.remove();
// Add in the styles
copy.append($styles.clone());
// Update title
if (options.title) {
var title = $("title", copy);
if (title.length === 0) {
title = $("<title />");
copy.append(title);
}
title.text(options.title);
}
// Appedned content
copy.append(getjQueryObject(options.append));
// Prepended content
copy.prepend(getjQueryObject(options.prepend));
if (options.manuallyCopyFormValues) {
// Manually copy form values into the HTML for printing user-modified input fields
// http://stackoverflow.com/a/26707753
copy.find("input")
.each(function () {
var $field = $(this);
if ($field.is("[type='radio']") || $field.is("[type='checkbox']")) {
if ($field.prop("checked")) {
$field.attr("checked", "checked");
}
} else {
$field.attr("value", $field.val());
}
});
copy.find("select").each(function () {
var $field = $(this);
$field.find(":selected").attr("selected", "selected");
});
copy.find("textarea").each(function () {
// Fix for https://github.com/DoersGuild/jQuery.print/issues/18#issuecomment-96451589
var $field = $(this);
$field.text($field.val());
});
}
// Get the HTML markup string
var content = copy.html();
// Notify with generated markup & cloned elements - useful for logging, etc
try {
options.deferred.notify('generated_markup', content, copy);
} catch (err) {
console.warn('Error notifying deferred', err);
}
// Destroy the copy
copy.remove();
if (options.iframe) {
// Use an iframe for printing
try {
printContentInIFrame(content, options);
} catch (e) {
// Use the pop-up method if iframe fails for some reason
console.error("Failed to print from iframe", e.stack, e.message);
printContentInNewWindow(content, options);
}
} else {
// Use a new window for printing
printContentInNewWindow(content, options);
}
return this;
};
})(jQuery);
How would I run the first one wait 5 or so seconds and then run the jquery print? I'm having a hard time with this. So the id would run first and then the print would run adter the id="abc" Here is an example of the code in use:
<div id="test">
<button id="abc" class="btn" onclick="jQuery.print(#test1)"></button>
</div>
If I understand your problem correctly, you want the jQuery click function to be run first, making a div with id="test1" visible and then, once it's visible, you want to run the onclick code which calls jQuery.print.
The very first thing I will suggest is that you don't have two different places where you are handling the click implementation, that can make your code hard to follow.
I would replace your $('#abc').click with the following:
function printDiv(selector) {
$(selector).show();
window.setTimeout(function () {
jQuery.print(selector);
}, 1);
}
This function, when called, will call jQuery.show on the passed selector, wait 1ms and then call jQuery.print. If you need the timeout to be longer, just change the 1 to whatever you need. To use the function, update your example html to the following:
<div id="test">
<button id="abc" class="btn" onclick="printDiv('#test1')"</button>
</div>
When the button is clicked, it will now call the previously mentioned function and pass it the ID of the object that you want to print.
As far as your second function goes, where you have the comment **//run this second**, you should leave that alone. All it does is extend you jQuery object with the print functionality. You need it to run straight away and it currently does.
In my project, I'm trying to upload an image, read it as a dataURL and store it into my database.
my HTML ng-click's call these methods with either true or false as a parameter (one does body image and the other a profile image)
Depending on whether true or false is called, it changes the id's for the event.
For some reason, when its "true" everything works perfectly fine. The user can pick the image, it gets sent to the database properly, and the image is loaded properly.
However, when "false" is called, I get the error:
[$rootScope:inprog] $apply already in progress
and then the console.log() test "2222" does not get called.
Html
<img src="{{controller.myCause.causeImage}}" ng-click="controller.uploadImage('true')" alt="Your Image Here" class="cause-img" id="profileImage" style="width: 80%; height: 35%;" height="300" />
<input id="imageUpload" type='file'>
<img src="{{controller.myCause.causeThumbnailImage}}" ng-click="controller.uploadImage('false')" alt="Your Thumbnail Here" id="profileImageThumb" class="cause-img" style="width: 80%; height: 20%;" height="200" />
<input id="profileImageUpload" type='file'>
Client Side
/////////////////////////////////////
//Begin Uploading Cause Profile Image
public uploadImage(bool) {
if (bool === "true") {
$("#imageUpload").click();
this.imgUpload(bool);
} else {
$("#profileImageThumb").click();
this.imgUpload(bool);
}
}
public imgUpload(bool) {
console.log(bool);
var _this = this;
var id = "";
var imgId = "";
if (bool === "true") {
id = "#imageUpload";
imgId = "#profileImage";
} else {
id = "#profileImageUpload";
imgId = "#profileImageThumb";
}
console.log("111");
$(id).change(function () {
console.log("2222");
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.readAsDataURL(this.files[0]);
$(imgId).attr('src', this.files[0]);
_this.convertFileToBase64AndSet(this.files, bool);
}
});
};
convertFileToBase64AndSet(fileList: FileList, bool) {
var self = this;
if (fileList.length > 0) {
var reader = new FileReader();
reader.onloadend = (e: Event) => {
self.postProfileImage(reader.result, bool);
}
reader.readAsDataURL(fileList[0]);
}
}
public postProfileImage(file, bool) {
if (bool === "true") {
this.$http.put(`api/causes/profpic`, JSON.stringify(file)).then((res) => {
this.$state.reload();
});
} else {
this.$http.put(`api/causes/thumbpic`, JSON.stringify(file)).then((res) => {
this.$state.reload();
});
}
}
//End Cause Profile Image Upload
////////////////////////////////
Your click event is running digest cycle which is conflicting with current digest cycle.
you can use setTimeout()
Can you try this
public uploadImage(bool) {
if (bool === "true") {
setTimeout(function({$("#imageUpload").click();})
this.imgUpload(bool);
} else {
setTimeout(function({$("#profileImageThumb").click();})
this.imgUpload(bool);
}
}
Similar to #hasan's answer, I got to this one.
public uploadImage(bool) {
var self = this;
var id = "";
if (bool === "true") {
id = "#imageUpload";
} else {
id = "#profileImageUpload";
}
setTimeout( () => {
$(id).click();
self.imgUpload(bool);
});
}
Thank you Hasan for pointing me in the right direction. setTimeout does indeed work!
Is it possible to simulate/fake the drop event using javascript only? How to test this type of event?
Take for example this dnd upload sample page , is it possible to trigger the "drop" event with a file without actually dropping a file there? Let's say clicking on a button?
I have started writing a Sukuli script that can control the mouse and do the trick but I was looking for a better solution.
EDIT
#kol answer is a good way to get rid of the drag and drop event but I still have to manually select a file from my computer. This is the bit I am interested in simulating. Is there a way to create a file variable programatically?
var fileInput = document.getElementById('fileInput'),
file = fileInput.files[0];
1. Dropping image selected by the user
I've made a jsfiddle. It's a stripped-down version of the html5demos.com page you've referred to, but:
I added an <input type="file"> tag which can be used to select an image file from the local computer, and
I also added an <input type="button"> tag with an onclick handler, which simulates the "drop file" event by directly calling the ondrop event handler of the DND-target div tag.
The ondrop handler looks like this:
holder.ondrop = function (e) {
this.className = '';
e.preventDefault();
readfiles(e.dataTransfer.files);
}
That is, we have to pass an argument to ondrop, which
has a dataTransfer field with a files array subfield, which contains the selected File, and
has a preventDefault method (a function with no body will do).
So the onclick handler of the "Simulate drop" button is the following:
function simulateDrop() {
var fileInput = document.getElementById('fileInput'),
file = fileInput.files[0];
holder.ondrop({
dataTransfer: { files: [ file ] },
preventDefault: function () {}
});
}
Test
Select an image file (png, jpeg, or gif)
Click on the "Simulate drop" button
Result
2. Dropping autogenerated test files without user interaction (GOOGLE CHROME ONLY!!!)
I've made another jsfiddle. When the page is loaded, a function gets called, which:
creates a text file into the temporary file system, and
loads and drops this text file into the target <div>; then
creates an image file into the temporary file system, and
loads and drops this image file into the target <div>.
The code of this drop-simulator function call is the following:
(function () {
var fileErrorHandler = function (e) {
var msg = "";
switch (e.code) {
case FileError.QUOTA_EXCEEDED_ERR:
msg = "QUOTA_EXCEEDED_ERR";
break;
case FileError.NOT_FOUND_ERR:
msg = "NOT_FOUND_ERR";
break;
case FileError.SECURITY_ERR:
msg = "SECURITY_ERR";
break;
case FileError.INVALID_MODIFICATION_ERR:
msg = "INVALID_MODIFICATION_ERR";
break;
case FileError.INVALID_STATE_ERR:
msg = "INVALID_STATE_ERR";
break;
default:
msg = "Unknown Error";
break;
};
console.log("Error: " + msg);
},
requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem,
dropFile = function (file) {
holder.ondrop({
dataTransfer: { files: [ file ] },
preventDefault: function () {}
});
};
if (!requestFileSystem) {
console.log("FileSystem API is not supported");
return;
}
requestFileSystem(
window.TEMPORARY,
1024 * 1024,
function (fileSystem) {
var textFile = {
name: "test.txt",
content: "hello, world",
contentType: "text/plain"
},
imageFile = {
name: "test.png",
content: "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
contentType: "image/png",
contentBytes: function () {
var byteCharacters = atob(this.content),
byteArrays = [], offset, sliceSize = 512, slice, byteNumbers, i, byteArray;
for (offset = 0; offset < byteCharacters.length; offset += sliceSize) {
slice = byteCharacters.slice(offset, offset + sliceSize);
byteNumbers = new Array(slice.length);
for (i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
return byteArrays;
}
};
// Create and drop text file
fileSystem.root.getFile(
textFile.name,
{ create: true },
function (fileEntry) {
fileEntry.createWriter(
function (fileWriter) {
fileWriter.onwriteend = function(e) {
console.log("Write completed (" + textFile.name + ")");
fileSystem.root.getFile(
textFile.name,
{},
function (fileEntry) {
fileEntry.file(
function (file) {
dropFile(file);
},
fileErrorHandler
);
},
fileErrorHandler
);
};
fileWriter.onerror = function(e) {
console.log("Write failed (" + textFile.name + "): " + e.toString());
};
fileWriter.write(new Blob([ textFile.content ], { type: textFile.contentType }));
},
fileErrorHandler
);
},
fileErrorHandler
);
// Create and drop image file
fileSystem.root.getFile(
imageFile.name,
{ create: true },
function (fileEntry) {
fileEntry.createWriter(
function (fileWriter) {
fileWriter.onwriteend = function(e) {
console.log("Write completed (" + imageFile.name + ")");
fileSystem.root.getFile(
imageFile.name,
{},
function (fileEntry) {
fileEntry.file(
function (file) {
dropFile(file);
},
fileErrorHandler
);
},
fileErrorHandler
);
};
fileWriter.onerror = function(e) {
console.log("Write failed (" + imageFile.name + "): " + e.toString());
};
fileWriter.write(new Blob(imageFile.contentBytes(), { type: imageFile.contentType }));
},
fileErrorHandler
);
},
fileErrorHandler
);
},
fileErrorHandler
);
})();
The content of the auto-generated text file is given as a string, and the content of the image file is given as a base64-encoded string. These are easy to change. For example, the test text file can contain not just plain text, but HTML too. In this case, don't forget to change the textFile.contentType field from text/plain to text/html, and to add this content type to the acceptedTypes array and to the previewfile function. The test image can also be changed easily, you just need an image-to-base64 converter.
I had to extend the drop handler code to handle text files in addition to images:
acceptedTypes = {
'text/plain': true, // <-- I added this
'image/png': true,
'image/jpeg': true,
'image/gif': true
},
...
function previewfile(file) {
if (tests.filereader === true && acceptedTypes[file.type] === true) {
var reader = new FileReader();
if (file.type === 'text/plain') { // <-- I added this branch
reader.onload = function (event) {
var p = document.createElement("p");
p.innerText = event.target.result;
holder.appendChild(p);
}
reader.readAsText(file);
} else {
reader.onload = function (event) {
var image = new Image();
image.src = event.target.result;
image.width = 250; // a fake resize
holder.appendChild(image);
};
reader.readAsDataURL(file);
}
} else {
holder.innerHTML += '<p>Uploaded ' + file.name + ', ' + file.size + ' B, ' + file.type;
console.log(file);
}
}
Note that after loading the jsfiddle, the autogenerated files can be listed for debugging purposes:
Result
The screenshot shows that the simulated drop inserted the content of the autogenerated text file before the autogenerated image. The HTML code of the DND-target <div> looks like this:
<div id="holder" class="">
<p>hello, world</p>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggkFBTzlUWEwwWTRPSHdBQUFBQkpSVTVFcmtKZ2dnPT0=" width="250">
</div>
#kol answer is a good way to get rid of the drag and drop event but I
still have to manually select a file from my computer. This is the bit
I am interested in simulating. Is there a way to create a file
variable programatically? -caiocpricci2
Try this
function createFile() {
var create = ["<!doctype html><div>file</div>"];
var blob = new Blob([create], {"type" : "text/html"});
return ( blob.size > 0 ? blob : "file creation error" )
};
createFile()
I'm working on hybrid mobile app using html5/js. It has a function download zip file then unzip them. The download function is not problem but I don't know how to unzip file (using javascript).
Many people refer to zip.js but it seems only reading zip file (not unzip/extract to new folder)
Very appreciate if someone could help me !!!
Have a look at zip.js documentation and demo page. Also notice the use of JavaScript filesystem API to read/write files and create temporary files.
If the zip file contains multiple entries, you could read the zip file entries and display a table of links to download each individual file as in the demo above.
If you look the source of the demo page, you see the following code (code pasted from Github demo page for zip.js) (I've added comments to explain):
function(obj) {
//Request fileSystemObject from JavaScript library for native support
var requestFileSystem = obj.webkitRequestFileSystem || obj.mozRequestFileSystem || obj.requestFileSystem;
function onerror(message) {
alert(message);
}
//Create a data model to handle unzipping and downloading
var model = (function() {
var URL = obj.webkitURL || obj.mozURL || obj.URL;
return {
getEntries : function(file, onend) {
zip.createReader(new zip.BlobReader(file), function(zipReader) {
zipReader.getEntries(onend);
}, onerror);
},
getEntryFile : function(entry, creationMethod, onend, onprogress) {
var writer, zipFileEntry;
function getData() {
entry.getData(writer, function(blob) {
var blobURL = creationMethod == "Blob" ? URL.createObjectURL(blob) : zipFileEntry.toURL();
onend(blobURL);
}, onprogress);
}
//Write the entire file as a blob
if (creationMethod == "Blob") {
writer = new zip.BlobWriter();
getData();
} else {
//Use the file writer to write the file clicked by user.
createTempFile(function(fileEntry) {
zipFileEntry = fileEntry;
writer = new zip.FileWriter(zipFileEntry);
getData();
});
}
}
};
})();
(function() {
var fileInput = document.getElementById("file-input");
var unzipProgress = document.createElement("progress");
var fileList = document.getElementById("file-list");
var creationMethodInput = document.getElementById("creation-method-input");
//The download function here gets called when the user clicks on the download link for each file.
function download(entry, li, a) {
model.getEntryFile(entry, creationMethodInput.value, function(blobURL) {
var clickEvent = document.createEvent("MouseEvent");
if (unzipProgress.parentNode)
unzipProgress.parentNode.removeChild(unzipProgress);
unzipProgress.value = 0;
unzipProgress.max = 0;
clickEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.href = blobURL;
a.download = entry.filename;
a.dispatchEvent(clickEvent);
}, function(current, total) {
unzipProgress.value = current;
unzipProgress.max = total;
li.appendChild(unzipProgress);
});
}
if (typeof requestFileSystem == "undefined")
creationMethodInput.options.length = 1;
fileInput.addEventListener('change', function() {
fileInput.disabled = true;
//Create a list of anchor links to display to download files on the web page
model.getEntries(fileInput.files[0], function(entries) {
fileList.innerHTML = "";
entries.forEach(function(entry) {
var li = document.createElement("li");
var a = document.createElement("a");
a.textContent = entry.filename;
a.href = "#";
//Click event handler
a.addEventListener("click", function(event) {
if (!a.download) {
download(entry, li, a);
event.preventDefault();
return false;
}
}, false);
li.appendChild(a);
fileList.appendChild(li);
});
});
}, false);
})();
})(this);