Last active
June 2, 2016 15:36
-
-
Save wegorich/1681b7ff17054ebf8873794ef4aec5ed to your computer and use it in GitHub Desktop.
samples form file uploader speach
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$.ajax({ | |
xhr: ()=> { | |
var xhr = new window.XMLHttpRequest(); | |
xhr.upload.addEventListener("progress", (e)=> console.log(e.loaded / e.total) //progress | |
, false); | |
return xhr; | |
}, | |
processData: false | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.box__dragndrop, | |
.box__uploading, | |
.box__success, | |
.box__error { | |
display: none; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<form class="box" method="post" action="" enctype="multipart/form-data"> | |
<div class="box__input"> | |
<input class="box__file" type="file" name="files[]" id="file" data-multiple-caption="{count} files selected" multiple /> | |
<label for="file"><strong>Choose a file</strong><span class="box__dragndrop"> or drag it here</span>.</label> | |
<button class="box__button" type="submit">Upload</button> | |
</div> | |
<div class="box__uploading">Uploading…</div> | |
<div class="box__success">Done!</div> | |
<div class="box__error">Error! <span></span>.</div> | |
</form> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
;( function ( document, window, index ) | |
{ | |
// feature detection for drag&drop upload | |
var isAdvancedUpload = function() | |
{ | |
var div = document.createElement( 'div' ); | |
return ( ( 'draggable' in div ) || ( 'ondragstart' in div && 'ondrop' in div ) ) && 'FormData' in window && 'FileReader' in window; | |
}(); | |
// applying the effect for every form | |
var forms = document.querySelectorAll( '.box' ); | |
Array.prototype.forEach.call( forms, function( form ) | |
{ | |
var input = form.querySelector( 'input[type="file"]' ), | |
label = form.querySelector( 'label' ), | |
errorMsg = form.querySelector( '.box__error span' ), | |
restart = form.querySelectorAll( '.box__restart' ), | |
droppedFiles = false, | |
showFiles = function( files ) | |
{ | |
label.textContent = files.length > 1 ? ( input.getAttribute( 'data-multiple-caption' ) || '' ).replace( '{count}', files.length ) : files[ 0 ].name; | |
}, | |
triggerFormSubmit = function() | |
{ | |
var event = document.createEvent( 'HTMLEvents' ); | |
event.initEvent( 'submit', true, false ); | |
form.dispatchEvent( event ); | |
}; | |
// letting the server side to know we are going to make an Ajax request | |
var ajaxFlag = document.createElement( 'input' ); | |
ajaxFlag.setAttribute( 'type', 'hidden' ); | |
ajaxFlag.setAttribute( 'name', 'ajax' ); | |
ajaxFlag.setAttribute( 'value', 1 ); | |
form.appendChild( ajaxFlag ); | |
// automatically submit the form on file select | |
input.addEventListener( 'change', function( e ) | |
{ | |
showFiles( e.target.files ); | |
triggerFormSubmit(); | |
}); | |
// drag&drop files if the feature is available | |
if( isAdvancedUpload ) | |
{ | |
form.classList.add( 'has-advanced-upload' ); // letting the CSS part to know drag&drop is supported by the browser | |
[ 'drag', 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave', 'drop' ].forEach( function( event ) | |
{ | |
form.addEventListener( event, function( e ) | |
{ | |
// preventing the unwanted behaviours | |
e.preventDefault(); | |
e.stopPropagation(); | |
}); | |
}); | |
[ 'dragover', 'dragenter' ].forEach( function( event ) | |
{ | |
form.addEventListener( event, function() | |
{ | |
form.classList.add( 'is-dragover' ); | |
}); | |
}); | |
[ 'dragleave', 'dragend', 'drop' ].forEach( function( event ) | |
{ | |
form.addEventListener( event, function() | |
{ | |
form.classList.remove( 'is-dragover' ); | |
}); | |
}); | |
form.addEventListener( 'drop', function( e ) | |
{ | |
droppedFiles = e.dataTransfer.files; // the files that were dropped | |
showFiles( droppedFiles ); | |
triggerFormSubmit(); | |
}); | |
} | |
// if the form was submitted | |
form.addEventListener( 'submit', function( e ) | |
{ | |
// preventing the duplicate submissions if the current one is in progress | |
if( form.classList.contains( 'is-uploading' ) ) return false; | |
form.classList.add( 'is-uploading' ); | |
form.classList.remove( 'is-error' ); | |
if( isAdvancedUpload ) // ajax file upload for modern browsers | |
{ | |
e.preventDefault(); | |
// gathering the form data | |
var ajaxData = new FormData( form ); | |
if( droppedFiles ) | |
{ | |
Array.prototype.forEach.call( droppedFiles, function( file ) | |
{ | |
ajaxData.append( input.getAttribute( 'name' ), file ); | |
}); | |
} | |
// ajax request | |
var ajax = new XMLHttpRequest(); | |
ajax.open( form.getAttribute( 'method' ), form.getAttribute( 'action' ), true ); | |
ajax.onload = function() | |
{ | |
form.classList.remove( 'is-uploading' ); | |
if( ajax.status >= 200 && ajax.status < 400 ) | |
{ | |
var data = JSON.parse( ajax.responseText ); | |
form.classList.add( data.success == true ? 'is-success' : 'is-error' ); | |
if( !data.success ) errorMsg.textContent = data.error; | |
} | |
else alert( 'Error. Please, contact the webmaster!' ); | |
}; | |
ajax.onerror = function() | |
{ | |
form.classList.remove( 'is-uploading' ); | |
alert( 'Error. Please, try again!' ); | |
}; | |
ajax.send( ajaxData ); | |
} | |
else // fallback Ajax solution upload for older browsers | |
{ | |
var iframeName = 'uploadiframe' + new Date().getTime(), | |
iframe = document.createElement( 'iframe' ); | |
$iframe = $( '<iframe name="' + iframeName + '" style="display: none;"></iframe>' ); | |
iframe.setAttribute( 'name', iframeName ); | |
iframe.style.display = 'none'; | |
document.body.appendChild( iframe ); | |
form.setAttribute( 'target', iframeName ); | |
iframe.addEventListener( 'load', function() | |
{ | |
var data = JSON.parse( iframe.contentDocument.body.innerHTML ); | |
form.classList.remove( 'is-uploading' ) | |
form.classList.add( data.success == true ? 'is-success' : 'is-error' ) | |
form.removeAttribute( 'target' ); | |
if( !data.success ) errorMsg.textContent = data.error; | |
iframe.parentNode.removeChild( iframe ); | |
}); | |
} | |
}); | |
// restart the form if has a state of error/success | |
Array.prototype.forEach.call( restart, function( entry ) | |
{ | |
entry.addEventListener( 'click', function( e ) | |
{ | |
e.preventDefault(); | |
form.classList.remove( 'is-error', 'is-success' ); | |
input.click(); | |
}); | |
}); | |
// Firefox focus bug fix for file input | |
input.addEventListener( 'focus', function(){ input.classList.add( 'has-focus' ); }); | |
input.addEventListener( 'blur', function(){ input.classList.remove( 'has-focus' ); }); | |
}); | |
}( document, window, 0 )); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import helpers from '../upload-helpers'; | |
export const STATE_IN_PROGRESS = "In Progress"; | |
export const STATE_PENDING = "Pending"; | |
export const STATE_COMPLETED = "Completed"; | |
export class FileDTO { | |
constructor(file, entryDTO: EntryDTO, done) { | |
this.file = file; | |
this.entryDTO = entryDTO; | |
this.folderDTO = entryDTO.folderDTO; | |
this.bites = entryDTO.folderDTO.bites; | |
this.strategy = this.folderDTO.strategy; | |
this.done = done; | |
} | |
static of(file, entryDTO, done) { | |
return new FileDTO(file, entryDTO, done); | |
} | |
getPath() { | |
return this.strategy.getPath(helpers.getNameSalted(this.file.name, this.entryDTO.times)); | |
} | |
getName() { | |
return this.strategy.getName(this.file, helpers.getNameSaltedAndExt(this.file.name, this.entryDTO.times)) | |
} | |
getType(){ | |
return this.strategy.getType(this.file, this.isXLS); | |
} | |
} | |
export class EntryDTO { | |
constructor(data) { | |
Object.assign(this, data); | |
this.uploaded = 0; | |
this.errors = 0; | |
this.warnings = 0; | |
this.resumables = []; | |
this.progress = { | |
timestamp: new Date().getTime(), | |
timePerFile: 0, | |
timeLeft: 0 | |
} | |
// var getDirName = (this.item.fullPath || this.item.webkitRelativePath).split('/'); | |
// getDirName.splice(getDirName.length - 1, 1); | |
// this.dirName = getDirName.join('/'); | |
} | |
static of(data) { | |
return new EntryDTO(data); | |
} | |
} | |
export class FolderDTO { | |
constructor() { | |
this.files = []; | |
this.folders = []; | |
this.totalFiles = 0; | |
this.foldersCount = 0; | |
this.resumables = []; | |
this.bites = { total: 0, perFile: 0 }; | |
} | |
static of() { | |
return new FolderDTO(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import async from 'async'; | |
import { EntryDTO } from './dto'; | |
import moment from 'moment'; | |
export class FolderFiles { | |
typeName = ""; | |
regex = null; | |
//it's for just file picker upload, without drag and drop | |
addItem(item, metadata, obj) { | |
let name = item.relativePath || item.webkitRelativePath || item.name; | |
return EntryDTO.of({ | |
item: item, | |
count: 1, | |
metadata: metadata, | |
ext: name.substr(name.lastIndexOf('.') + 1), | |
itemName: name, | |
folderDTO: obj | |
}); | |
} | |
entriesToArray(list) { | |
return Array.prototype.slice.call(list || [], 0); | |
} | |
filterFiles(obj, folderDTO) { | |
var items = []; | |
for (let o in obj) { | |
o = obj[o]; | |
items.push(this.addItem(o, null, folderDTO)); | |
} | |
return items.filter((i) => i.item.size); | |
} | |
filterFile(entry, metadata) { | |
return (metadata ? metadata.size : entry.size) && (!this.regex || this.regex && this.regex.test(entry.relativePath || entry.webkitRelativePath || entry.name)); | |
} | |
filterEntries(entries, obj = { foldersCount: 0, files: [], folders: [] }) { | |
entries.forEach((entry) => { | |
if (!entry) return; | |
if (entry.isFile) { | |
entry.getMetadata((metadata) => { | |
if (this.filterFile(entry, metadata)) { | |
obj.files.push(this.addItem(entry, metadata, obj)); | |
} | |
}); | |
} | |
if (entry.isDirectory) { | |
obj.foldersCount += 1; | |
obj.folders.push(entry.createReader()); | |
} | |
}); | |
return obj; | |
} | |
scanFolder(reader, entries) { | |
entries = entries || []; | |
reader.readEntries((results) => { | |
// This recursion, it works cause we receive just an links array | |
// We upload files one by one | |
// The uploaded one removed from memory | |
if (results.length) //read next 100 files | |
setTimeout(() => this.scanFolder(reader, entries.concat(this.entriesToArray(results)))); | |
else { | |
this.filterEntries(entries, this.scanned); | |
this.scanNextFolder(); | |
} | |
//fail callback | |
}, this.scanNextFolder.bind(this)); | |
} | |
scanEntries(reader) { //fail callback | |
reader.readEntries(this.scanFolder.bind(this, reader), this.scanNextFolder.bind(this)); | |
} | |
scanNextFolder() { | |
if (this.scanned.folders.length) { | |
this.scanEntries(this.scanned.folders.pop()); | |
} else { | |
this.scanned.endCb(this.scanned); | |
} | |
} | |
scanEntriesSync(entries, folderDTO, endCb) { | |
this.scanned = this.filterEntries(entries, Object.assign(folderDTO, { | |
foldersCount: 0, | |
totalFiles: 0, | |
files: [], | |
folders: [], | |
endCb: endCb | |
})); | |
this.scanNextFolder(); | |
} | |
scan(e, folderDTO) { | |
folderDTO = folderDTO || {}; | |
return new Promise((resolve, reject) => { | |
if (e.target && e.target.files) { | |
resolve(Object.assign(folderDTO, { | |
files: this.filterFiles(e.target.files, folderDTO), | |
totalFiles: e.target.files.length, | |
foldersCount: 1 | |
})); | |
} | |
if (e.dataTransfer && e.dataTransfer.items) { | |
//e.dataTransfer.items isn't enumerable | |
this.scanEntriesSync($.map(e.dataTransfer.items, (value, index) => [value]).map(e => e.webkitGetAsEntry()), folderDTO, (data) => { | |
delete folderDTO.folders; | |
delete folderDTO.endCb; | |
resolve(Object.assign(folderDTO, { | |
files: data.files, | |
totalFiles: data.files.length, | |
foldersCount: data.foldersCount | |
})); | |
}); | |
} | |
}); | |
} | |
getPath(fileName, folderPath) { | |
return this.path || `${folderPath}/${moment().format("YYYY/MM/DD")}`; | |
} | |
getName(file, saltedName) { | |
return saltedName; | |
} | |
getType(file) { | |
return file.type; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<form id="uploadForm" | |
enctype="multipart/form-data" | |
action="/api/potato" | |
method="post"> | |
<input type="file" name="directory[]" webkitdirectory /> | |
<input type="submit" value="Upload" /> | |
</form> | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<form id="uploadForm" | |
enctype="multipart/form-data" | |
action="/api/potato" | |
method="post"> | |
<input type="file" name="potato-head" multiple/> | |
<input type="file" name="potato-head" /> | |
<input type="submit" value="Upload"> | |
</form> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
throttle(callback) { | |
this.bitesPerSecond(); | |
if (this.selectedFolder.bites.perSeconds <= parseInt(this.speedLimit)) | |
callback(); | |
else | |
setTimeout(callback, this.selectedFolder.bites.perSeconds / parseInt(this.speedLimit) * 1000); | |
} | |
uploadFileItem(entryDTO, file, callback) { | |
this.throttle(() => { | |
this.uploadFile.upload(FileDTO.of(file, entryDTO, () => callback())); | |
}); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class UploadFile { | |
constructor() { | |
this.baseUrl = './'; | |
} | |
cleunUpResumble(resumable) { | |
resumable.cancel(); | |
resumable.events.length = 0; | |
} | |
processFile(id, file, entryDTO, bites, callback) { | |
let folderDTO = entryDTO.folderDTO; | |
var resumable = new Resumable({ | |
target: `${this.baseUrl}upload/files/${id}/chunks`, | |
forceChunkSize: true, | |
permanentErrors: [400, 404, 415], | |
simultaneousUploads: 1, | |
testChunks: false, | |
chunkSize: 7000000 //7 mb | |
}); | |
resumable.on('fileSuccess', () => { | |
entryDTO.uploaded += 1; | |
this.helpers.updateFileETA(entryDTO); | |
bites.total += bites.perFile; | |
bites.perFile = 0; | |
this.cleunUpResumble(resumable); | |
callback && callback({ result: { id: id } }); | |
}); | |
resumable.on('fileProgress', (file) => { | |
this.helpers.calcETA(entryDTO, bites, { | |
lengthComputable: true, | |
loaded: resumable.getSize() * resumable.progress(), | |
total: file.size | |
}) | |
if (entryDTO.metadata) { | |
this.helpers.updateFileETA(entryDTO); | |
bites.perFile = entryDTO.metadata.uploaded = resumable.getSize() * resumable.progress(); | |
} | |
}); | |
resumable.on('fileError', (file, message) => { | |
entryDTO.errors += 1; | |
entryDTO.retries = entryDTO.retries || 0; | |
this.helpers.updateFileETA(entryDTO); | |
bites.total += bites.perFile; | |
bites.perFile = 0; | |
entryDTO.timeout = 0; | |
this.cleunUpResumble(resumable); | |
callback && callback(); | |
}); | |
resumable.assignDrop({ | |
addEventListener: function(t, l, u) { | |
l.call(this, { | |
stopPropagation: function() {}, | |
preventDefault: function() {}, | |
dataTransfer: { | |
files: [file] | |
} | |
}); | |
} | |
}); | |
folderDTO.resumables = folderDTO.resumables || []; | |
folderDTO.resumables.push(resumable); | |
setTimeout(() => resumable.upload(), 0); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let params = { | |
"meta": { | |
"fileName": file.name, | |
<-- some data //--> | |
"userId": this.user.id | |
}, | |
"name": folder.path + file.name, | |
"type": file.type | |
}; | |
$.ajax({ | |
url: `/upload/files`, | |
type: 'POST', | |
data: JSON.stringify(params), | |
contentType: 'application/blabla', | |
dataType: 'json' | |
}).done(data => { | |
<-- Start chank uploading //--> | |
}).fail((jqXHR, textStatus, err) => { | |
<-- Make retry or log error logic //-->}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment