js-file-uploader: HTML5 + Javascript asyncronous file upload.
v. 3 - Massimo Cassandro - (c) 2017/2021
JS-File-Uploader is a small javascript utility (less than 8kb minified and gzipped) that simplifies uploading files to HTML pages.
Uploads are performed asynchronously via an Ajax call to a server-side script that must register the file and return a JSON string.
Although the default settings are based on Bootstrap 4, FileUploader is entirely and easily configurable from scratch and can be adapted to any layout.
The demo folder contains many examples of FileUploader using.
Works with all modern browser (tested thanks to Browserstack Open Source program).
- Provide the user with a better interface and feedback than the standard input-file element: it can instantly show a preview of the uploaded file (if it is image) and show some information like name, size etc.
- Provide an easy way to add constraints to file format, aspect ratio, size and weight
- Perform multiple uploads in file-by-file way, thus avoiding any server-side limitations on upload size
- Although the default settings are based on Bootstrap 4, JS-File-Uploader is entirely and easily configurable from scratch and can be adapted to any layout.
- All string messages can be customized using the desired language (the default language is Italian and an English translation is included in the distribution).
The demo folder contains many examples of FileUploader using.
JS-File-Uploader needs a modern browser and is not compatible with Internet Explorer.
Anyway, providing a fallback input[type="file"]
field (recommended), JS-File-Uploader silently degrade to the input element. You just have to provide that the server-side processing includes this possibility.
JS-File-Uploader is tested with many browsers and mobile devices (thanks to Browserstack Open Source program).
JS-File-Uploader can be installed using npm:
npm i @massimo-cassandro/js-file-uploader
You can add JS-File-Uploader importing the bundled ES module version contained in the dist
folder, or the original source from src
:
// bundled ESM version
import FileUploader from `@massimo-cassandro/js-fileuploader/dist/FileUploader.esm.min.js`;
// source version
import FileUploader from `@massimo-cassandro/js-fileuploader/src/file_uploader`;
All the demo pages load JS-File-Uploader from src
.
You can also use the UMD version directly in your browser:
<script src="path/to/FileUploader.umd.min.js"></script>
<!-- optional language -->
<script src="path/to/en.umd.min.js"></script>
FileUploader.umd.min.js
exposes the FileUploader
global variable, and en.umd.min.js
exposes fupl_strings_en
. Take a look to the UMD demo for a detailed example.
You also need to add some css to make JS-File-Uploader work.
The best and easiest way is to use the SCSS files contained in the scss
directory. However there is a compiled .css
file into dist
.
You just need to import the fileUploader.scss
file into your project. To customize colors and other properties, change the values of the variables listed in _fupl-default-variables.scss
before importing:
$fupl-outline-color: #ddd;
$fupl-outline-color-dragover: #e4d70d;
$fupl-background-color: #fbfbfb;
// ...
import 'scss/fileUploader.scss';
To start FileUploader you must first add it to your pages as explained before, and then instantiate it:
FileUploader({
selector : [string] // selector of fileuploader elements (default `.file-uploader2`)
options : [object] // custom options
css : [string] // css url
local_strs : [object] // localized strings
})
where:
selector
is a string to use as a selector for FileUploader elements. It can be aclass
, anid
or any other valid selector. FileUploader will be automatically activated on these items. The default is.file-uploader2
options
is an object of various parameters to modify the behaviors of FileUploader. This object contains the only required configuration option:uploader_url
, the server-side scripting URL needed to save remote files. Read the [FileUploader Options] (options) chapter for details on all parameters.css
if the optional url for the css file used by FileUploader. Since it can be joined with your project css file, this parameter can benull
.local_strs
is an object that contains localized strings for FileUploader interface. Read the Languages paragraph in this page for more info.
Therefore, a really minimal FileUploader setup could be:
<div class="file-uploader2"></div>
<script type="module">
import FileUploader from '@massimo-cassandro/js-fileuploader/dist/FileUploader.esm.min.js';
FileUploader({
options: {
uploader_url: '/path/to/server-side-script'
}
})
</script>
However, it is very likely that you will need to configure additional parameters, add a fallback for older browsers or improve accessibility. For a more complete explanation of FileUploader configuration, read the Setup chapter.
JS-File-Uploader main language is Italian. An english translation is provided (but you may need to make some adjustments...).
To add other languages, you need to translate ʻen.js or ʻit.js
(in the src/i18n
directory) and use the new file when starting JS-File-Uploader:
import FileUploader from `@massimo-cassandro/js-fileuploader/src/file_uploader`;
// exposes the var `my_lang`
import fupl_strings_en from 'path/to/new_language.js';
FileUploader({
options: {
// ... options ...
},
local_strs: my_lang
});
You can also send a pull request for you translation file, to have it included in future JS-File-Uploader distributions.
FileUploader requires a server-side script to manage file uploading.
The goal of FileUploader is to allow asynchronous uploading of files, saving them definitively on server only after the form containing the input file has been submitted. The script must place the file in a temporary directory (eg tmp
), giving it a temporary name.
After form submit, uploaded files have to be be moved to their final destination: this way any unsubmitted form will not leave any orphaned file, since the files left in the tmp
directory should be periodically deleted.
When a file is uploaded, FileUploader receives from the server side script, the information necessary to complete the registration, inserts it in a series of hidden inputs, and returns it to the server when the form is submitted.
For each file selected, FileUploader send to server-side script a POST call with file data. PHP $_FILES
variable, for example, returns to the script something like this:
Array
(
[file] => Array
(
[name] => myfile.jpg
[type] => image/jpeg
[tmp_name] => /tmp/php/phpGu39M5
[error] => 0
[size] => 8772
)
)
The script must process the request (save the file in tmp
directory), and return a json formatted as follows:
{
"tmp_file": "path/to/tmp/file",
"error": null
}
where:
tmp_file
is the path to the temporary file registered on the servererror
isnull
on success, or a string of the error message produced by the server.
Then FileUploader will build a hidden field for each of these parameters:
tmp_file
: name of temporary file (as returned by the json response)file_name
: name of the original file (web normalized)size
: file size (bytes)type
: file mimetype
In case of images, the width
and height
values of the image itself will also be present.
All values will be used to create some hidden fields whose name
attribute will consist of:
- the value of the
varname
parameter - an unique id corresponding to the file
- the variable name
Example (with varname='custom_file'
and unique id equal to __unique_id__
):
<input type="hidden" name="custom_file[__unique_id__][tmp_file]" value="...">
<input type="hidden" name="custom_file[__unique_id__][file_name]" value="...">
[...]
A demo including a server side sample script can be found in the repository at demo/php_sample
The minimal needed markup is
<div data-file-uploader></div>
Anyway, to ensure accessibility and graceful degrations is strongly recommended to add a fallback input field:
<div data-file-uploader>
<input type="file" name="fallback-field">
</div>
When FileUploader is started, the internal field is replaced with a generated one. But if it fails, by adding the inner field you can ensure the basic functionality of your form.
FileUploader configuration is based on parameters defined in src/_default_options.js
The parameters can be overridden according to this cascading sequence:
- Values of
_default_options.js
file are the default ones - Parameters assigned in the
FileUploader()
obj argument override the default ones, and are valid for all the FileUploader elements involved - Parameters assigned to each FileUploader instance, using
data-*
attributes, prevail and override the previous ones: in this way it is possible to have different behaviors also on the same page.
Finally, if an input [type="file"]
field is present inside the FileUploader element, any accept
, required
, multiple
or disabled
attributes are taken into account in the configuration .
For example, you can set a FileUploader as required either by setting the data-required="true"
attribute of the FileUploader element, or by using the required
attribute of the file field.
Data attributes can be set as single items (e.g. data-disabled="true"
) or as a json object to be assigned to the data-file-uploader
attribute:
<div data-file-uploader='{"disabled": true, "multiple": true}'>
NB: if the
required
,multiple
ordisabled
parameters are set totrue
in the FileUploader instance, it is not possible to set them tofalse
via the input field attributes.
See _default_options.js
for a complete list of all parameters.
When a pre-registered file is removed from FileUploader, a hidden field is generated.
It contains the id of the removed file as value
and the delete_varname
parameter as name
.
You must provide the necessary server side script to definitively remove the file from the server.
<input type="hidden" name="delete_file_var[]" value="123">
FileUploader configuration parameter allow you to radically change the generated layout.
To perform a complete customization of the layout, you can modify both the css and much of the markup. furthermore, some callbacks can be set to perform additional operations.
When a FileUploader is generated, the fupl
class is added to the original element (black bordered in the following diagram), and some additional markup is then generated inside and outside it. The internal one (blue bordered) is completely configurable by modifying the templates parameters.
Take care to preserve elements with
fupl-*
classes, that are requested by FileUploader
If specific behaviors are required, you can define some callback functions that will be invoked when certain events occur:
-
init_callback
(default null): called after the initialization of each FileUploader element. The argument of this function is an object containing all the options of the instance -
upload_start_callback
(default null): function called whenever a file is sent to the server. The function is invoked by passing an object containing:item
: object. It contains:id
: unique ID of the itemfile
: current * filelist * objectwidth
andheight
: null or dimensions in pixels of the imagetmp_file
: temporary file name assigned by the server side script
img_preview
: image thumbnail as a Base64 string (null if it is of other types)options
: options of current FileUploader instance
-
upload_complete_callback
(default null): function called whenever a file is loaded. The function is invoked by passing an object containing:item
: object. It contains:id
: unique ID of the itemfile
: current * filelist * objectwidth
andheight
: null or dimensions in pixels of the imagetmp_file
: temporary file name assigned by the server side script
server_error
: null, if the upload was completed successfully, or string with the returned error messageoptions
: options of current FileUploader instance
-
alternative_loading_func
(default null): Not a real callback, but an alternative function to display the upload progress. If present, it replaces the standard one. It is invoked with two parameters:progress_event
: progress upload eventoptions
: options of current FileUploader instance
FileUploader can be integrated with Fancybox (v. 3), simply setting
the fancybox
option to true
.
Fancybox application files are not loaded by FileUploader and have to be present in the HTML page before FileUploader loads.
Fancybox is applied only to previously registered images.
As soon as an item is added to the uploader, an Ajax request is sent to the server for file registration. When the operation is completed, the server returns a json with the data of the recorded file, as indicated in the previous points.
From the moment the request is sent and until the server responds, the user can still submit the form, but in this case all the hidden elements related to the uploading file that will not be will not be present, therefore they will noit be registered
To avoid this problem, you can enable the disable_submit
option which disables the *Submit * button of form until the server has sent its response.
However, this option is not sufficient to ensure that problems of this type are avoided (in some cases, the user could submit with the Enter key), and it is also possible that other settings re-enable the button regardless of the upload outcome.
The safest solution is therefore to block the submit if there are elements with the fupl-is-uploading
class, class assigned to each new element added to the uploader and deleted upon completion of loading:
let myForm = document.getElementById('myForm');
myForm.addEventListener('submit', () => {
if(myForm.querySelectorAll('.fupl-is-uploading').length) {
alert('Loading not completed');
return false;
}
});
If FileUploader is used in multiple pages, it is possible to set a centralized control on all form elements:
document.querySelectorAll('form').forEach( this_form => {
this_form.addEventListener('submit', () => {
if(this_form.querySelectorAll('.fupl-is-uploading').length) {
alert('Wait for the image upload to complete!');
return false;
}
});
});
The module src-utilities/check_uncompleted_uploads.js
is already ready for this purpose. Check the uncompleted upload demo page
However, implementing a server-side control too is certainly a good idea.
Since a file can be loaded via Drag & Drop, it is not possible to use the native required
to check for mandatory files.
However, this check can be performed using the data
attributes added dynamically to the .fupl-wrapper
element: data-required="true"
and data-has-values="true|false"
.
Therefore, to verify the required content of the # my-uploader
element, it is very simple:
document.getElementById('my-uploader')
.closest('.fupl-wrapper:not([disabled])[data-required="true"][data-has-values="true"]') !== null
Or, to check the entire form:
document.querySelectorAll('.fupl-wrapper:not([disabled])[data-required="true"][data-has-values="false"]').length === 0
You can also import the ready-to-use scr-utilities/check_required_uploader.js
script in your project. Take a look at the required uploader demo page
The examples in the demo folder load FileUploader directly from the ES6 modules in the src
folder, but you can easily use one of the UMD version present in the “dist” folder (see the UMD version demo) or the compiled all-in-one ES6 module version.
They also do not use a server-side engine (so the form tag is not present, except in some cases) and therefore the server response is simulated thru a static json file (demo/demo-assets/server-side-demo-response.json
). However, there is a simple php example of server-side application (in /demo/php_sample
folder) that you can look at.
The demo uses some extra tools that are not needed in your projects: Prism and js-beautify).
For better clarity, they area managed with dedicated files, all contained in demo/demo-assets
folder.
For the same reason, both Bootstrap and JQuery are loaded from CDNs when needed.
All the css and the js files really related to FileUploader are located in demo/css
and demo/js
folders.
Each demo page consists of 5 boxes:
- The first is the real demo, which is an example of a FileUploader implementation
- The Original Markup box contains the html code used to activate FileUploader
- The Javascript box contains the code that calls the ES6 FileUploader library and instantiates the demo. The code is located in the JS file shown in parentheses
- The Generated markup box contains the html code after Fileuploader parsing
- The last box, Parsed FileUploader options, contains the object generated merging (in this order):
- the default options object (in
src/_default_options.js
) - the options set when FileUploader was instantiated (you can see them in the Javascript box)
- the configuration retrieved from data attributes of
data-file-uploader
element. This object is the same you can see in the browser console, and both them are visible only whendebug
option is set totrue
.
- the default options object (in
There is a long list of future implementation and fix I've planned, take a look at https://github.com/massimo-cassandro/js-file-uploader/issues/4.
- https://css-tricks.com/drag-and-drop-file-uploading/
- https://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/
- https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Limiting_the_size_of_a_file_before_its_upload
- https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/
- https://codepen.io/therealDaze/pen/ZaoErp
- https://github.com/gridstack/gridstack.js
- https://developer.mozilla.org/it/docs/Web/API/HTML_Drag_and_Drop_API
- https://www.html5rocks.com/en/tutorials/dnd/basics/
- https://kryogenix.org/code/browser/custom-drag-image.html