FileAPI — a set of tools for working with files.
~~~ DEMO
~~~ user pic
~~~
Multiupload: all browsers that support HTML5 or Flash
Drag'n'Drop upload: files (HTML5) & directories (Chrome 21+)
Upload one file: all browsers
Working with Images: IE6+, FF 3.6+, Chrome 10+, Opera 11.1+, Safari 5.4+
crop, resize, preview & rotate (HTML5 or Flash)
auto orientation by exif (HTML5, if include FileAPI.exif.js or Flash)
< span class ="js-fileapi-wrapper " style ="position: relative; ">
< input id ="user-files " type ="file " multiple />
</ span >
< div id ="preview-list ">
</ div >
var input = document . getElementById ( 'user-files' ) ;
var previewNode = document . getElementById ( 'preview-list' ) ;
// Drag'n'Drop
FileAPI . event . dnd ( previewNode , function ( over ) {
$ ( this ) . css ( 'background' , over ? 'red' : '' ) ;
} , function ( files ) {
// ..
} ) ;
FileAPI . event . on ( input , 'change' , function ( evt ) {
var files = FileAPI . getFiles ( evt . target ) ; // or FileAPI.getFiles(evt)
// filtering
FileAPI . filterFiles ( files , function ( file , info ) {
if ( / image/ . test ( file . type ) && info ) {
return info . width >= 320 && info . height >= 240 ;
}
else {
return file . size > 128 ;
}
} , function ( fileList , ignor ) {
if ( ignor . length ) {
// ...
}
if ( ! fileList . length ) {
// empty file list
return ;
}
// do preview
var imageList = FileAPI . filter ( fileList , function ( file ) { return / image/ . test ( file . type ) ; } ) ;
FileAPI . each ( imageList , function ( imageFile ) {
FileAPI . Image ( imageFile )
. preview ( 100 , 120 )
. get ( function ( err , image ) {
if ( err ) {
// ...
}
else {
previewNode . appendChild ( image ) ;
}
} )
;
} ) ;
// upload on server
var xhr = FileAPI . upload ( {
url : '...' ,
data : { foo : 'bar' } , // POST-data (iframe, flash, html5)
headers : { 'x-header' : '...' } , // request headers (flash, html5)
files : {
files : FileAPI . filter ( fileList , function ( file ) { return ! / image/ . test ( file . type ) ; } ) ,
pictures : imageList
} ,
imageTransform : {
maxWidth : 1024 ,
maxHeight : 768
} ,
imageAutoOrientation : true ,
fileprogress : function ( evt ) { // (flash, html5)
var percent = evt . loaded / evt . total * 100 ;
// ...
} ,
progress : function ( evt ) { // (flash, html5)
var percent = evt . loaded / evt . total * 100 ;
// ...
} ,
complete : function ( err , xhr ) {
// ...
}
} ) ;
} ) ;
} ) ;
HTML structure (templates)
FileAPI.getFiles (source:HTMLInput|Event
):Array
FileAPI.getDropFiles (files:Array
, callback:Function
)
FileAPI.filterFiles (files:Array
, iterator:Function
, complete:Function
)
FileAPI.upload (options:Object
):XMLHttpRequest
FileAPI.getInfo (file:File
, callback:Function
)
FileAPI.readAsImage (file:File
, callback:function
)
FileAPI.readAsDataURL (file:File
, callback:function
)
FileAPI.readAsBinaryString (file:File
, callback:function
)
FileAPI.readAsArrayBuffer (file:File
, callback:function
)
FileAPI.readAsText (file:File
, callback:function
)
FileAPI.readAsText (file:File
, encoding:String
, callback:function
)
FileAPI.event.on(el:HTMLElement
, eventType:String
, fn:Function
)
FileAPI.event.off(el:HTMLElement
, eventType:String
, fn:Function
)
FileAPI.event.one(el:HTMLElement
, eventType:String
, fn:Function
)
FileAPI.event.dnd(el:HTMLElement
, onHover:Function
, onDrop:Function
)
jQuery('#el').dnd(onHover, onDrop)
.crop(width[, height])
.crop(x, y, width[, height])
.resize(width[, height])
.resize(width, height, type:Enum(min,max,preview)
)
.preview(width[, height])
.rotate(deg)
.get(fn:Function
)
FileAPI.KB
FileAPI.MB
FileAPI.GB
FileAPI.TB
FileAPI.support.html5:Boolean
FileAPI.support.cors:Boolean
FileAPI.support.dnd:Boolean
FileAPI.support.flash:Boolean
FileAPI.support.canvas:Boolean
FileAPI.support.dataURI:Boolean
FileAPI.each(obj:Object|Array
, fn:function
, context:Mixed
)
FileAPI.extend(dst:Object
, src:Object
):Object
FileAPI.filter(list:Array
, iterator:Function
):Array
FileAPI.isFile(file:Mixed
):Boolean
FileAPI.toBinaryString(str:Base64
):String
< script > var FileAPI = { staticPath : '/js/' } ; </ script >
< script src ="/js/FileAPI.min.js "> </ script >
FileAPI . event . on ( '#my-file-1' , 'change' , onSelect ) ;
// or jQuery
$ ( '#my-file-2' ) . on ( 'change' , onSelect ) ;
function onSelect ( evt /**Event*/ ) {
// (1) extract fileList from event
var files = FileAPI . getFiles ( evt ) ;
// (2) or so
var files = FileAPI . getFiles ( evt . target ) ;
}
function onDrop ( evt ) {
FileAPI . getDropFiles ( evt , function ( files ) {
if ( files . length ) {
// ...
}
} ) ;
}
// OR
var el = document . getElementById ( 'el' ) ;
FileAPI . event . dnd ( el , function ( over /**Boolean*/ , evt /**Event*/ ) {
el . style . background = ever ? 'red' : '' ;
} , function ( files /**Array*/ , evt /**Event*/ ) {
// ...
} ) ;
FileAPI . getInfo ( imageFile /**File*/ , function ( err /**Boolean*/ , info /**Object*/ ) {
if ( ! err ) {
switch ( info . exif . Orientation ) {
// ...
}
}
} ) ;
// ...
FileAPI . addInfoReader ( / ^image/ , function ( file /**File*/ , callback /**Function*/ ) {
// http://www.nihilogic.dk/labs/exif/exif.js
// http://www.nihilogic.dk/labs/binaryajax/binaryajax.js
FileAPI . readAsBinaryString ( file , function ( evt ) {
if ( evt . type == 'load' ) {
var binaryString = evt . result ;
var oFile = new BinaryFile ( binaryString , 0 , file . size ) ;
var exif = EXIF . readFromBinaryFile ( oFile ) ;
callback ( false , { 'exif' : exif || { } } ) ;
}
else if ( evt . type == 'error' ) {
callback ( 'read_as_binary_string' ) ;
}
else if ( evt . type == 'progress' ) {
// ...
}
} ) ;
} ) ;
FileAPI . filterFiles ( files , function ( file , info ) {
if ( / image/ . test ( file . type ) && info ) {
return info . width > 320 && info . height > 240 ;
}
return file . size < 10 * FileAPI . MB ;
} , function ( result , ignor ) {
// ...
} ) ;
FileAPI.readAsImage, FileAPI.readAsDataURL (FileAPI.readAsBinaryString)
FileAPI . readAsImage ( file , function ( evt ) {
if ( evt . type == 'load' ) {
var images = document . getElementById ( 'images' ) ;
images . appendChild ( evt . result ) ;
}
else {
// ...
}
} ) ;
FileAPI . readAsDataURL ( file , function ( evt ) {
if ( evt . type == 'load' ) {
// success
var result = evt . result ;
}
else if ( evt . type == 'progress' ) {
var pr = evt . loaded / evt . total * 100 ;
}
else {
// error
}
} ) ;
var xhr = FileAPI . upload ( {
url : '...' ,
data : { foo : 'bar' } ,
headers : { 'x-header' : '...' } ,
files : {
images : FileAPI . filter ( files , function ( file ) { return / image/ . test ( file . type ) ; } ) ,
customFile : { file : 'generate.txt' , blob : customFileBlob }
} ,
imageTransform : {
maxWidth : 1024 ,
maxHeight : 768
} ,
imageAutoOrientation : true ,
prepare : function ( file , options ) {
// prepare options for current file
options . data . filename = file . name ;
} ,
upload : function ( xhr , options ) {
// start uploading
} ,
fileupload : function ( xhr , options ) {
// start file uploading
} ,
fileprogress : function ( evt ) {
// progress file uploading
var filePercent = evt . loaded / evt . total * 100 ;
} ,
filecomplete : function ( err , xhr ) {
if ( ! err ) {
var response = xhr . responseText ;
}
} ,
progress : function ( evt ) {
// total progress uploading
var totalPercent = evt . loaded / evt . total * 100 ;
} ,
complete : function ( err , xhr ) {
if ( ! err ) {
// Congratulations, the uploading was successful!
}
}
} ) ;
width:Number
height:Number
preview:Boolean
maxWidth:Number
maxHeight:Number
rotate:Number
FileAPI . upload ( {
// ..
imageOriginal : false , // don't send original on server
imageTransform : {
// (1) Resize to 120x200
resize : { width : 120 , height : 200 }
// (2) create preview 320x240
thumb : { width : 320 , height : 240 , preview : true }
// (3) Resize by max side
max : { maxWidth : 800 , maxHeight : 600 }
// (4) Custom resize
custom : function ( info , transform ) {
return transform
. crop ( 100 , 100 , 300 , 200 )
. resize ( 100 , 50 )
;
}
}
} ) ;
// (1) all images
FileAPI . upload ( {
// ..
imageAutoOrientation : true
} ) ;
// (2) or so
FileAPI . upload ( {
// ..
imageAutoOrientation : true ,
imageTransform : { width : . . , height : . . }
} ) ;
// (3) or so
FileAPI . upload ( {
// ..
imageTransform : { rotate : 'auto' }
} ) ;
// (4) only "800x600", original not modified
FileAPI . upload ( {
// ..
imageTransform : {
"800x600" : { width : 800 , height : 600 , rotate : 'auto' }
}
} ) ;
{
name : 'fileName' ,
type : 'mime-type' ,
size : 'fileSize'
}
{
status : Number ,
statusText : Number ,
readyState : Number ,
response : Blob ,
responseXML : XML ,
responseText : String ,
responseBody : String ,
getResponseHeader : function ( name /**String*/ ) /**String*/ { } ,
getAllResponseHeaders : function ( ) /**Object*/ { } ,
abort : function ( ) { }
}
Cross-Domain upload-controller headers
<?
header ('Access-Control-Allow-Methods: POST, OPTIONS ' );
header ('Access-Control-Allow-Headers: Origin, X-Requested-With ' ); // and other custom headers
header ('Access-Control-Allow-Origin: ' . $ _SERVER ['HTTP_ORIGIN ' ]); // a comma-separated list of domains
if ( $ _SERVER ['REQUEST_METHOD ' ] == 'OPTIONS ' ){
exit ;
}
if ( $ _SERVER ['REQUEST_METHOD ' ] == 'POST ' ){
// ...
}
?>
/controller.php
?foo=bar
&images=...
&callback=...
<script type="text/javascript">
(function (ctx, jsonp){
if ( ctx && ctx[jsonp] ){
ctx[jsonp](<?= $ statusCode/*200 — OK*/ ?> , "<?= addslashes ($ statusText )?> ", "<?= addslashes ($ response )?> ");
}
})(this.parent, "<?= htmlspecialchars ($ _POST ['callback ' ])?> ");
</script>
HTML structure (templates)
< span class ="js-fileapi-wrapper " style ="position: relative; display: inline-block; ">
< input name ="files " type ="file " multiple />
</ span >
< style >
.upload-btn {
width: 130px;
height: 25px;
overflow: hidden;
position: relative;
border: 3px solid #06c;
border-radius: 5px;
background: #0cf;
}
.upload-btn:hover {
background: #09f;
}
.upload-btn__txt {
z-index: 1;
position: relative;
color: #fff;
font-size: 18px;
font-family: "Helvetica Neue";
line-height: 24px;
text-align: center;
text-shadow: 0 1px 1px #000;
}
.upload-btn__inp {
top: -10px;
right: -40px;
z-index: 2;
position: absolute;
cursor: pointer;
opacity: 0;
filter: alpha(opacity=0);
font-size: 50px;
}
</ style >
< div class ="upload-btn js-fileapi-wrapper ">
< div class ="upload-btn__txt "> Upload files</ div >
< input class ="upload-btn__inp " name ="files " type ="file " multiple />
</ div >
< style >
.upload-link {
color: #36c;
display: inline-block;
*zoom: 1;
*display: inline;
overflow: hidden;
position: relative;
padding-bottom: 2px;
text-decoration: none;
}
.upload-link__txt {
z-index: 1;
position: relative;
border-bottom: 1px dotted #36c;
}
.upload-link:hover .upload-link__txt {
color: #f00;
border-bottom-color: #f00;
}
.upload-link__inp {
top: -10px;
right: -40px;
z-index: 2;
position: absolute;
cursor: pointer;
opacity: 0;
filter: alpha(opacity=0);
font-size: 50px;
}
</ style >
< a class ="upload-link js-fileapi-wrapper ">
< span class ="upload-link__txt "> Upload photo</ span >
< input class ="upload-link__inp " name ="photo " type ="file " accept =".jpg,.jpeg,.gif " />
</ a >
#51: remove circular references from file-objects
(Flash transport)
added changelog