I'm using Dropzone to do a image check and tell the user if the resolution is good enough for production.
When files are in RAW format (dng, cr2, orf, etc), I want them to be automatically APPROVED (as if they where uploaded) but WITHOUT actually uploading them (RAW files are large and would take too long to upload).
Basically what I mean is that the file would stay in the list with a check-mark but would not be sent to php. Is this possible?
Currently I'm trying to refuse those files based on their type and then programmatically change them to "success". But I can't seem to find how.
var counter = 0;
myDropzone.on("sending", function(file) {
if(file.type == "image/cr2"){
myDropzone.files[counter].accepted = false;
}
counter++;
});
I don't think dropzone is going to identify the file type of those formats, but you can check if the filename contains the extension you want, here an example:
var formats = ['.orf', '.cr2', '.dng'];
Dropzone.autoDiscover = false;
var myDropzone = $('#yourDropzoneId').dropzone({
accept: function (file, done) {
var extension = '.' + file.name.toLowerCase().split('.').slice(-1)[0];
if (formats.indexOf(extension) >= 0) {
done('OK');
$('.dz-preview').last().toggleClass('dz-error dz-success');
}
else {
done();
}
}
});
In this example the files with a extension specified in the array will be rejected but then is going to simulate a success, the other files with a different extension will be uploaded.
Related
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(){ }
}
);
}
);
}
);
I'm making a website, in which I want to offer the user to download the whole website (CSS and images included) for them to modify. I know I can download individual resources with
Click Me
but like I said, this only downloads one file, whereas I would like to download the entire website.
If it helps you visualise what I mean: in chrome, IE and Firefox you can press ctrl+s to download the entire website (make sure you save it as Web page, Complete.
Edit: I know I can create a .zip file that it will download, however doing so requires me to update it every time I make a change, which is something I'd rather not do, as I could potentially be making a lot of changes.
As I mention, it is better that you will have a cron job or something like this that once in a while will create you a zip file of all the desired static content.
If you insist doing it in javascript at the client side have a look at JSZip .
You still have to find a way to get the list of static files of the server to save.
For instance, you can create a txt file with each line is a link to a webpage static file.
you will have to iterate over this file and use $.get to get it's content.
something like this:
// Get list of files to save (either by GET request or hardcoded)
filesList = ["f1.json /echo/jsonp?name=1", "inner/f2.json /echo/jsonp?name=2"];
function createZip() {
zip = new JSZip();
// make bunch of requests to get files content
var requests = [];
// for scoping the fileName
_then = (fname) => data => ({ fileName: fname, data });
for (var file of filesList) {
[fileName, fileUrl] = file.split(" ");
requests.push($.get(fileUrl).then(_then(fileName)));
}
// When all finished
$.when(...requests).then(function () {
// Add each result to the zip
for (var arg of arguments) {
zip.file(arg.fileName, JSON.stringify(arg.data));
}
// Save
zip.generateAsync({ type: "blob" })
.then(function (blob) {
saveAs(blob, "site.zip");
});
});
}
$("#saver").click(() => {
createZip();
});
JSFiddle
Personally, I don't like this approach. But do as you prefer.
I want to redirect the user to a different webpage after they click a hyperlink which allows them to download a file. However since they need to make a choice in the open/save file dialog, I don't want to redirect them until they accept the download.
How can I detect that they performed this action?
As i've found from years of maintaining download.js, there simply is no way to tell from JS (or likely in general, see below) what a user chooses to do with the download Open/Save dialog. It's a common feature request, and i've looked into it repeatedly over the years. I can say with confidence that it's impossible; I'll joyfully pay 10 times this bounty if someone can demo a mechanical way to determine the post-prompt user action on any file!
Further, it's not really just a matter of JS rules, the problem is complicated by the way browsers download and prompt such files. This means that even servers can't always tell what happened. There might be some specific work-arounds for a few specific cases, but they are not pretty or simple.
You could force your users to "re-upload" a downloaded file with an <input type=file> to validate it, but that's cumbersome at best, and the local file browse dialog could be alarming to some. It's the only sure-fire method to ensure a download, but for non-sensitive applications its very draconian, and it won't work on some "mobile" platforms that lack file support.
You might also try watching from the server side, pushing a message to the client that the file was hit on the server. The problem here is that downloads start downloading as soon as the Open/Save dialog appears, though invisibly in the background. That's why if you wait a few moments to "accept" a large file, it seems to download quickly at first. From the server's perspective, the activity is the same regardless of what the user does.
For a huge file, you could probably detect that the whole file was not transferred, which implies the user clicked "cancel", but it's a complicated syncing procedure pushing the status from backend to client. It would require a lot of custom programming with sockets, PHP message passing, EventSource, etc for little gain. It's also a race against time, and an uncertain amount of time at that; and slowing down the download is not recommended for user satisfaction.
If it's a small file, it will physically download before the user even sees the dialog, so the server will be useless. Also consider that some download manager extensions take over the job, and they are not guaranteed to behave the same as a vanilla browser. Forcing a wait can be treacherous to someone with a slow hard drive that takes "forever" to "finish" a download; we've all experienced this, and not being able to continue while the "spinny" winds down would lower user satisfaction, to put it mildly.
In short, there's no simple way, and really no way in general, except for huge files you know will take a long time to download. I've spent a lot of blood sweat and tears trying to provide my download.js users the ability, but there are simply no good options. Ryan dahl initially wrote node.js so he could provide his users an upload progress bar, maybe someone will make a server/client package to make it easy to do the same for downloads.
Here is a hacky solution.
My StreamSaver lib don't use blobs to download a file with a[download]. It uses service worker to stream something to the disc by emulating how the server handles download with content-disposition attachment header.
evt.respondWith(
new Response(
new ReadableStream({...})
)
)
Now you don't have any exact way of knowing what the user pressed in the dialog but you have some information about the stream. If the user press cancel in the dialog or abort the ongoing download then the stream gets aborted too.
The save button is trickier. But lets begin with what a stream bucket highWaterMark can tell us.
In my torrent example I log the writer.desiredSize. It's the correlation to how much data it is willing to receive. When you write something to the stream it will lower the desired size (whether it be a count or byte strategy). If it never increases then it means that user probably have paused the download. When it goes down below 0 then you are writing more data than what the user is asking for.
And every chunk write you do returns a promise
writer.getWriter().write(uint8).then(() => {
// Chunk have been sent to the destination bucket
// and desiredSize increase again
})
That promise will resolve when the bucket isn't full. But it dose not mean that the chunk have been written to the disc yet, it only means that the chunk has been passed from the one stream to another stream (from write -> readable -> respondWith) and will often do so in the beginning of the stream and when another earlier chunk have been written to the disc.
It's a possibility that the write stream can finishes even before the user makes a choice if the hole data can fit within the bucket (memory)
Tweaking the bucket size to be lower then the data can help
So you can make assumption on when the
download starts
finish
and pauses
but you won't know for sure since you don't get any events (apart from the abort that closes the stream)
Note that the torrent example don't show correct size if you don't have support for Transferable streams but you could get around this if you do everything inside a service worker. (instead of doing it in the main thread)
Detecting when the stream finish is as easy as
readableStream.pipeTo(fileStream).then(done)
And for future references WICG/native-file-system might give you access to write files to disc but it has to resolve a prompt dialog promise before you can continue and might be just what the user is asking for.
There are examples of saving a blob as a stream, and even more multiple blob's as a zip too if you are interested.
Given that user is, or should be aware that file should be downloaded before next step in process, user should expect some form of confirmation that file has been downloaded to occur.
You can create a unique idenfifier or timestamp to include within downloaded file name by utilizing <a> element with download attribute set to a the modified file name.
At click event of <button> element call .click() on <a> element with href set to a Blob URL of file. At a element click handler call .click() on an <input type="file"> element, where at attached change event user should select same file which was downloaded at the user action which started download of file.
Note the chaining of calls to .click() beginning with user action. See Trigger click on input=file on asynchronous ajax done().
If the file selected from user filesystem is equal to modified downloaded file name, call function, else notify user that file download has not been confirmed.
window.addEventListener("load", function() {
let id, filename, url, file;
let confirmed = false;
const a = document.querySelector("a");
const button = document.querySelector("button");
const confirm = document.querySelector("input[type=file]");
const label = document.querySelector("label");
function confirmDownload(filename) {
if (confirmed) {
filename = filename.replace(/(-\d+)/, "");
label.innerHTML = "download of " + filename + " confirmed";
} else {
confirmed = false;
label.innerHTML = "download not confirmed";
}
URL.revokeObjectURL(url);
id = url = filename = void 0;
if (!file.isClosed) {
file.close()
}
}
function handleAnchor(event) {
confirm.click();
label.innerHTML = "";
confirm.value = "";
window.addEventListener("focus", handleCancelledDownloadConfirmation);
}
function handleFile(event) {
if (confirm.files.length && confirm.files[0].name === filename) {
confirmed = true;
} else {
confirmed = false;
}
confirmDownload(filename);
}
function handleDownload(event) {
// file
file = new File(["abc"], "file.txt", {
type: "text/plain",
lastModified: new Date().getTime()
});
id = new Date().getTime();
filename = file.name.match(/[^.]+/g);
filename = filename.slice(0, filename.length - 1).join("")
.concat("-", id, ".", filename[filename.length - 1]);
file = new File([file], filename, {
type: file.type,
lastModified: id
});
a.download = filename;
url = URL.createObjectURL(file);
a.href = url;
alert("confirm download after saving file");
a.click();
}
function handleCancelledDownloadConfirmation(event) {
if (confirmed === false && !confirm.files.length) {
confirmDownload(filename);
}
window.removeEventListener("focus", handleCancelledDownloadConfirmation);
}
a.addEventListener("click", handleAnchor);
confirm.addEventListener("change", handleFile);
button.addEventListener("click", handleDownload);
});
<button>download file</button>
<a hidden>download file</a>
<input type="file" hidden/>
<label></label>
plnkr http://plnkr.co/edit/9NmyiiQu2xthIva7IA3v?p=preview
jquery.fileDownload allows you to do this:
$(document).on("click", "a.fileDownloadPromise", function () {
$.fileDownload($(this).prop('href'))
.done(function () { alert('File download a success!'); })
.fail(function () { alert('File download failed!'); });
return false;
});
Take a look at Github:
https://github.com/johnculviner/jquery.fileDownload
I had a project that I dabbled in recently that required me to specify whether a user could upload a particular kind of file, i.e. (a user can upload a png but not a pdf).
I may not used the most efficient method, but ultimately what I did was to code a small, built in "webapp" that functioned as a file browser, for upload or download.
I suppose the closest example without releasing my "secret project" would be https://encodable.com/filechucker/
Maybe you could write a simple integrated filebrowser such as that that cloud services use sometimes (i.e. dropbox) and have some functions that detect input with custom boxes and stuff.
Just a few thoughts.
window.showSaveFilePicker from the File System Access API does what you want, but unfortunately it's currently supported only by Chrome and Edge. It returns a promise -- if the user chooses to download the file the promise is resolved; if they cancel, an AbortError is raised.
try this:
<html>
<head>
<script type="text/javascript">
function Confirmation(pg) {
var res = confirm("Do you want to download?");
if(res){
window.open(pg,"_blank");
}
return res;
}
</script>
</head>
<body>
download
</body>
</html>
Say I have an array of user selected files.
In my application, each selected file corresponds to a person. Once the user selects the files, a table appears. In the left column is each file's name. In the right hand column, the user must fill in the name of the relevant person. Once all the names are filled in the user can upload the files. As there could be hundreds of names to fill in, I would like to be able to save the user's progress to localStorage periodically, in case the page refreshes.
I can pass each file object (var thisFile) to an uploading script and the browser will know where to locate the file.
However, I would like to be able to save the list of selected files in localStorage, and JSON.stringify will strip out a file object.
So say I first create a javascript object based on the file object (var file below).
If I then pass var file to the uploading script, will the browser still be able to locate the file, or would the full path to the file have been lost?
for (var i = 0; i < userSelectedFiles.length; i++) {
//File object (Stripped out by JSON.stringify).
var thisFile = userSelectedFiles[i];
//Create a javascript object based on the file object (Not stripped out by JSON.stringify).
var file: {
'lastModified': thisFile.lastModified,
'lastModifiedDate': thisFile.lastModifiedDate,
'name': thisFile.name,
'size': thisFile.size,
'type': thisFile.type
},
}
After more research, I have found that a file must be reselected after a browser refresh- the location of a file cannot be stored in localStorage.
You can save files to offline storage.
Read this article about offline storage offline_storage and quota-research
Read documentation LocalFileSystem
You can test offline storage here test page
How to use link
Here is the example for WebKit browsers (chrome and opera) of creating a file -> info.txt with your content, you need to ask a user of storage quota, in this example, is 10mb.
var maxSize = 1024 * 1024 * 10;
var onError = function() {};
var requestFs = window.requestFileSystem ||
window.webkitRequestFileSystem ||
window.mozRequestFileSystem ||
window.msRequestFileSystem || undefined;
var onRequest = function(grantedSize) {
requestFs(window.PERSISTENT, grantedSize, function(filesystem) {
filesystem.root.getFile("info.txt", {create: true}, function(DatFile) {
DatFile.createWriter(function(DatContent) {
var blob = /** your file or blob*/;
DatContent.write(blob);
});
});
}, onError)
};
navigator.webkitPersistentStorage.requestQuota(maxSize, onRequest, onError);
Ive read a lot about the filesystem API and HTML5, but i just couldn't find a working solution so i ask you guys:
I want to have a file upload form, drag drop or regular input box doesnt matter, however i want to select a file, and after uploading it should take the file or a whole folder and "upload" it to the filesystem located on the clients computer. The upload is in brackets because i actually want to copy the file/folder to the clients local file system.
Is it even possible? Because i want to make an application, where a user can upload his files such as music or large videos and movies to his local filesystem and edit/watch etc them in my application. I know i have to upload those big files i have to cut them into pieces and load them stacked up, but i just want to start little :)
Thanks in advance
There's indeed little information on this subject at the moment, so I put together an example that combines:
Using the webkitdirectory attribute on <input type="file">.
This allows the user to select a directory using an appropriate dialog box.
Using the Filesystem API.
This is about the sandboxed filesystem which allows you to store files on the client's machine.
Using the File API.
This is the API that allows you to read files. The files are accessible through an <input type="file"> element, through a transfer using drag and drop, or through the Filesystem API.
As these are currently only working nicely in Chrome, I used the webkit prefix where necessary.
http://jsfiddle.net/zLna6/3/
The code itself has comments which I hope are clear:
var fs,
err = function(e) {
throw e;
};
// request the sandboxed filesystem
webkitRequestFileSystem(
window.TEMPORARY,
5 * 1024 * 1024,
function(_fs) {
fs = _fs;
},
err
);
// when a directory is selected
$(":file").on("change", function() {
$("ul").empty();
// the selected files
var files = this.files;
if(!files) return;
// this function copies the file into the sandboxed filesystem
function save(i) {
var file = files[i];
var text = file ? file.name : "Done!";
// show the filename in the list
$("<li>").text(text).appendTo("ul");
if(!file) return;
// create a sandboxed file
fs.root.getFile(
file.name,
{ create: true },
function(fileEntry) {
// create a writer that can put data in the file
fileEntry.createWriter(function(writer) {
writer.onwriteend = function() {
// when done, continue to the next file
save(i + 1);
};
writer.onerror = err;
// this will read the contents of the current file
var fr = new FileReader;
fr.onloadend = function() {
// create a blob as that's what the
// file writer wants
var builder = new WebKitBlobBuilder;
builder.append(fr.result);
writer.write(builder.getBlob());
};
fr.onerror = err;
fr.readAsArrayBuffer(file);
}, err);
},
err
);
}
save(0);
});
$("ul").on("click", "li:not(:last)", function() {
// get the entry with this filename from the sandboxed filesystem
fs.root.getFile($(this).text(), {}, function(fileEntry) {
// get the file from the entry
fileEntry.file(function(file) {
// this will read the contents of the sandboxed file
var fr = new FileReader;
fr.onloadend = function() {
// log part of it
console.log(fr.result.slice(0, 100));
};
fr.readAsBinaryString(file);
});
}, err);
});
That is not possible, exactly, but your app can still probably work. Reading the file is possible through a file input form element, but writing the file back to disk is where you'll run into trouble.
The two ways your browser can write to disk are 1) downloading a file and 2) the HTML5 filesystem API. Option #1 obviously doesn't let your application choose the destination and option #2 only works with browser-created sandbox filesystems. That restriction might not be a deal-breaker for you -- it just means that the folders that your app uses will be buried somewhere in your browser's data files.
Also, the Filesystem API is currently Chrome-only (but it is an open standard). If you want cross-platform support, maybe you can use IndexedDB. You could use localStorage, but Chrome has a hard 5MB limit, which would be terrible for a media application.