m-file-uploader v2.0.0-alpha-0
m-file-uploader
HTML5 + Javascript asyncronous file upload. v. 2 - Massimo Cassandro - (c) 2017/2020
(previously: js-file-uploader)
under development
Fileuploader automates and 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.
Similarly, all the string messages can be customized using the desired language. Look at the _set_options.js file for a complete list of all available parameters.
Many examples can be found in the demo folder
Browser compatibility
FileUploader needs a modern browser and is not compatible with Internet Explorer. It also requires Edge 16 or higher.
If the browser is not compatible, an alert window is showed and FileUploader stops. The message shown is the one indicated in the alert_messages.unsuitable_browser
parameter
However, it is possible to silently degrade FileUploader and use the browser standard input[type="file"]
tag.
To perform this option, you need to:
- set
silent_degradation
option to true (default is false); - provide a fallback input tag inside the FileUploader element;
- set a fallback init function if necessary (
unsuitable_browser_callback
parameter). This can be useuful to activate specific fallback behaviours; - add the
enctype
attribute to your form (FileUploader doesn't need it) - provide the necessary server side scripting. Very likely, the script to be used in this situation differs from the one used in the Ajax procedure.
Take a look at the silent degradation demo.
Codekit
All distribution files are built using Codekit, but you can easily switch to other tools, if you need.
Localization
Actually, all localization strings are part of default_options
object (in _set_options.js
). The default language is italian.
To change language, you can provide the desidered strings in the init
function (as in demo files) or you can edit the default_options
objects and rebuild dist files.
Furthermore, you have to set the locales
parameter to the desidered value to correctly represent numeric values.
Installation
FileUploader can be installed thru npm:
npm i --save js-file-uploader
Using FileUploader
Once file_uploader-min.js
has been added to your HTML page, FileUploader must be started setting up some parameters using the init
function.
Minimal setup:
<script src="file_uploader-min.js"></script>
FileUploader.init({
uploader_url : 'path/to/server/script'
});
The argument of`init is an object that sets some parameters. A complete list of them is described in _set_options.js file.
Once initialized, FileUploader is applied to all element with the data-file-uploader
attribute:
--
The demo
folder contains several FileUploader examples.
NB: The default selector is
data-file-uploader
, but, if necessary, thefile-uploader
part can be replaced with any string (with dataset compatible syntax, see https://developer.mozilla.org/en-US/demo/Web/API/HTMLElement/dataset) using thefupl_selector
option:
FileUploader.init({
uploader_url : 'path/to/server/script',
fupl_selector : 'my-uploader'
});
Markup
To activate FileUploader, this minimum markup is required:
<div data-file-uploader></div>
FileUploader will be activated with the global parameters defined in _set_options.js and in FileUploader.init()
argument.
However, it is possible to customize each individual instance using data attributes matching the same global parameters.
For example, if you want to use a specific server-side script (different from the globally defined one) for a specific instance, you can indicate it this way:
<div data-file-uploader data-uploader_url="path/to/alternate/server/script"></div>
You can also define parameters using a json string to be assigned directly to data-file-uploader
attribute:
<div data-file-uploader='{"uploader_url": "path/to/alternate/server/script"}'></div>
Note that some parameters, such as
css
, make sense only if defined globally.
Finally, it is possible to insert a input type="file"]
field inside the data-file-uploader
element, as a fallback for older browsers (see the Browser compatibility paragraph).
If FileUploader starts correctly, the input field is removed, otherwise it can be used as a standard HTML field.
To simplify configuration, all attributes of input file (multiple, required, accept, disabled) are passed to FileUploader, so you haven't to set it twice.
For example, to activate the multiple
option, you can define it using the data-multiple="true"
attribute in the FileUploader element, or adding the multiple
attribute to the input type="file"]
element (if present).
In case of conflict, generally, the last element prevails (see Parameters setting paragraph).
<div data-file-uploader>
<input type="file" name="fallback">
</div>
CSS
The default css file is based on Bootstrap 4 (not included), but you can change it, even changing, if necessary, the generated markup, through the templates
parameter (see _set_options.js(js/_set_options js)).
It is also possible to include the FileUploader css in your project style sheets; in this case it is sufficient to set the css
parameter to null
(the default value).
All changes must take care to preserve the class names prefixed with
fupl-
.
Parameters setting
FileUploader configuration is based on parameters defined in _set_options.js.
The parameters can be overridden according to this cascading sequence:
- Values of
_set_options.js
file are the default ones - Parameters assigned in
FileUploader.init()
override the default ones, and are valid for all the FileUploader elements involved - Parameters assigned to each FileUploader instance 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
ordisabled
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 therequired
attribute of the file field.
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.
Server-side script
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
andheight
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="...">
[...]
Deleting a pre-registered file
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">
Customization
FileUploader configuration parameter allow you to radical change the generated layout.
To perform a complete customization of the layout, you can modify both the css and much of the markup (mostly the items inside the templates
parameter).
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
Callback
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 instanceunsuitable_browser_callback
(default null): function called if an unsuitable browser is detectedupload_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 event -options
: options of current FileUploader instance
Fancybox integration
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.
Recipes
Check for completed loading
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;
}
});
});
Using jQuery:
$('form').each(function() {
$(this).submit(function() {
if($('.fupl-is-uploading', this).length) {
alert('Devi attendere che il caricamento delle immagini sia completato');
return false;
}
});
});
However, implementing a server-side control too is certainly a good idea.
Unsuitable browsers blocking
To prevent the submission of forms in non-compatible browsers (jQuery):
FileUploader.init({
[...]
/*
adds the class 'unsuitable_browser'
and removes the submit button, but does not prevent form submit
*/
unsuitable_browser_callback: function () {
$('[data-file-uploader]')
.closest('form')
.addClass('unsuitable_browser')
.find(':submit').each( function() {
$(this).replaceWith( '<div class="alert alert-danger my-4">You are using an incompatible browser</div>' );
});
}
});
// stop form submit
$('form').each(function() {
$(this).submit(function() {
if($(this).hasClass('unsuitable_browser')) {
alert("You are using an incompatible browser. Cannot load images");
return false;
}
});
});
required
fields checking
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
Known issues
- Incorrect handling of drag events with extra fields
TODO
- convert to es6 modules!!!
- device cameras integration
- svg img type
- more dist versions: es6 modules, umd etc,
- easier localization settings
- explicit uploader activation (??)
- add option to generate download link for images
- public method to check if uploader has values
- predefined extra fields templates for common uses
- alternative_loading_func severe testing
- fix incorrect handling of drag events with extra fields
- specific texts for touch devices
- easier interface for public sites
- one-level option obj or add the ability to change a deep key (i.e. using something like https://github.com/davidcalhoun/deep-object-assign-with-reduce to perform a deep assign)
- option to add an aspect ratio constrain
- option to add info/help text
References (and inspirations)
- 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
4 years ago