Repository: efeiefei/node-file-manager Branch: master Commit: b28dd01f2279 Files: 13 Total size: 35.7 KB Directory structure: gitextract_ghzjq0v0/ ├── .gitignore ├── LICENSE ├── README.md ├── bin/ │ └── node-file-manager.sh ├── lib/ │ ├── fileManager.js │ ├── fileMap.js │ ├── index.js │ ├── public/ │ │ └── js/ │ │ ├── angular-file.js │ │ └── app.js │ ├── routes.js │ ├── tools.js │ └── views/ │ └── files.html └── package.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules/ .idea ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2016 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Screen Shot ![screenshot](https://raw.githubusercontent.com/efeiefei/node-file-manager/master/example/screenshot.png) # Usage ```sh npm install -g node-file-manager node-file-manager -p 8080 -d /path/to/ ``` Or ```sh git clone https://github.com/efeiefei/node-file-manager.git cd node-file-manager npm i cd lib node --harmony index.js -p 8080 -d /path/to ``` We can run node-file-manager in terminal directly. We can specify prot add data root dir by `-p` and `-d`, default with 5000 and scripts directory. Then, we can view localhost:8080/ in our browr. ================================================ FILE: bin/node-file-manager.sh ================================================ #!/usr/bin/env sh SHELL_PATH=`dirname $0` cd $SHELL_PATH/../lib/node_modules/node-file-manager/lib node --harmony index.js $* ================================================ FILE: lib/fileManager.js ================================================ var fs = require('co-fs'); var co = require('co'); var fse = require('co-fs-extra'); var path = require('path'); var JSZip = require('jszip'); var FileManager = {}; FileManager.getStats = function *(p) { var stats = yield fs.stat(p); return { folder: stats.isDirectory(), size: stats.size, mtime: stats.mtime.getTime() } }; FileManager.list = function *(dirPath) { var files = yield fs.readdir(dirPath); var stats = []; for (var i=0; i= 0){ var e = new Error('Do Not Contain .. in relPath!'); e.status = 400; throw e; } else { return path.join(DATA_ROOT, relPath); } }; ================================================ FILE: lib/index.js ================================================ #!/usr/bin/env node var koa =require('koa'); var path = require('path'); var tracer = require('tracer'); var mount = require('koa-mount'); var morgan = require('koa-morgan'); var koaStatic = require('koa-static'); // Config var argv = require('optimist') .usage([ 'USAGE: $0 [-p ] [-d ]'] ) .option('port', { alias: 'p', 'default': 5000, description: 'Server Port' }) .option('directory', { alias: 'd', description: 'Root Files Directory' }) .option('version', { alias: 'v', description: 'Server Version' }) .option('help', { alias: 'h', description: "Display This Help Message" }) .argv; if (argv.help) { require('optimist').showHelp(console.log); process.exit(0); } if (argv.version) { console.log('FileManager', require('./package.json').version); process.exit(0); } global.C = { data: { root: argv.directory || path.dirname('.') }, logger: require('tracer').console({level: 'info'}), morganFormat: ':date[iso] :remote-addr :method :url :status :res[content-length] :response-time ms' }; // Start Server var Tools = require('./tools'); var startServer = function (app, port) { app.listen(port); C.logger.info('listening on *.' + port); }; var app = koa(); app.proxy = true; app.use(Tools.handelError); app.use(Tools.realIp); app.use(morgan.middleware(C.morganFormat)); var IndexRouter = require('./routes'); app.use(mount('/', IndexRouter)); app.use(koaStatic(path.join(__dirname,'./public/'))); startServer(app, +argv.port); ================================================ FILE: lib/public/js/angular-file.js ================================================ /** * ur.file: Native HTML5-based file input bindings for AngularJS * * @version 0.9a * @copyright (c) 2013 Union of RAD, LLC http://union-of-rad.com/ * @license: BSD */ /** * The ur.file module implements native support for file uploads in AngularJS. */ angular.module('ur.file', []).config(['$provide', function($provide) { /** * XHR initialization, copied from Angular core, because it's buried inside $HttpProvider. */ var XHR = window.XMLHttpRequest || function() { try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {} try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {} throw new Error("This browser does not support XMLHttpRequest."); }; /** * Initializes XHR object with parameters from $httpBackend. */ function prepXHR(method, url, headers, callback, withCredentials, type, manager) { var xhr = new XHR(); var status; xhr.open(method, url, true); if (type) { xhr.type = type; headers['Content-Type'] = type; } angular.forEach(headers, function(value, key) { (value) ? xhr.setRequestHeader(key, value) : null; }); manager.register(xhr); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { manager.unregister(xhr); var response = xhr.response || xhr.responseText; callback(status = status || xhr.status, response, xhr.getAllResponseHeaders()); } }; if (withCredentials) { xhr.withCredentials = true; } return xhr; } /** * Hook into $httpBackend to intercept requests containing files. */ $provide.decorator('$httpBackend', ['$delegate', '$window', 'uploadManager', function($delegate, $window, uploadManager) { return function(method, url, post, callback, headers, timeout, wc) { var containsFile = false, result = null, manager = uploadManager; if (post && angular.isObject(post)) { containsFile = hasFile(post); } if (angular.isObject(post)) { if (post && post.name && !headers['X-File-Name']) { headers['X-File-Name'] = encodeURI(post.name); } angular.forEach({ size: 'X-File-Size', lastModifiedDate: 'X-File-Last-Modified' }, function(header, key) { if (post && post[key]) { if (!headers[header]) headers[header] = post[key]; } }); } if (post && post instanceof Blob) { return prepXHR(method, url, headers, callback, wc, post.type, manager).send(post); } $delegate(method, url, post, callback, headers, timeout, wc); }; }]); /** * Checks an object hash to see if it contains a File object, or, if legacy is true, checks to * see if an object hash contains an element. */ var hasFile = function(data) { for (var n in data) { if (data[n] instanceof Blob) { return true; } if ((angular.isObject(data[n]) || angular.isArray(data[n])) && hasFile(data[n])) { return true; } } return false; }; /** * Prevents $http from executing its default transformation behavior if the data to be * transformed contains file data. */ $provide.decorator('$http', ['$delegate', function($delegate) { var transformer = $delegate.defaults.transformRequest[0]; $delegate.defaults.transformRequest = [function(data) { return data instanceof Blob ? data : transformer(data); }]; return $delegate; }]); }]).service('fileHandler', ['$q', '$rootScope', function($q, $rootScope) { return { /** * Loads a file as a data URL and returns a promise representing the file's value. */ load: function(file) { var deferred = $q.defer(); var reader = angular.extend(new FileReader(), { onload: function(e) { deferred.resolve(e.target.result); if (!$rootScope.$$phase) $rootScope.$apply(); }, onerror: function(e) { deferred.reject(e); if (!$rootScope.$$phase) $rootScope.$apply(); }, onabort: function(e) { deferred.reject(e); if (!$rootScope.$$phase) $rootScope.$apply(); } // onprogress: Gee, it'd be great to get some progress support from $q... }); reader.readAsDataURL(file); return angular.extend(deferred.promise, { abort: function() { reader.abort(); } }); }, /** * Returns the metadata from a File object, including the name, size and last modified date. */ meta: function(file) { return { name: file.name, size: file.size, lastModifiedDate: file.lastModifiedDate }; }, /** * Converts a File object or data URL to a Blob. */ toBlob: function(data) { var extras = {}; if (data instanceof File) { extras = this.meta(data); data = data.toDataURL(); } var parts = data.split(","), headers = parts[0].split(":"), body; if (parts.length !== 2 || headers.length !== 2 || headers[0] !== "data") { throw new Error("Invalid data URI."); } headers = headers[1].split(";"); body = (headers[1] === "base64") ? atob(parts[1]) : decodeURI(parts[1]); var length = body.length, buffer = new ArrayBuffer(length), bytes = new Uint8Array(buffer); for (var i = 0; i < length; i++) { bytes[i] = body.charCodeAt(i); } return angular.extend(new Blob([bytes], { type: headers[0] }), extras); } }; }]).service('uploadManager', ['$rootScope', function($rootScope) { angular.extend(this, { id : null, uploads: {}, capture: function(id) { this.id = id; this.uploads[id] = { loaded: 0, total: 0, percent: 0, object: null }; }, register: function(xhr) { if (this.id === null) { return false; } xhr._idXhr = this.id; this.id = null; this.uploads[xhr._idXhr]['object'] = xhr; var self = this; xhr.upload.onprogress = function(e) { if (e.lengthComputable) { self.uploads[xhr._idXhr]['loaded'] = e.loaded; self.uploads[xhr._idXhr]['total'] = e.total; self.uploads[xhr._idXhr]['percent'] = Math.round(e.loaded / e.total * 100); $rootScope.$apply(); } }; return true; }, unregister: function(xhr) { delete this.uploads[xhr._idXhr]; }, get: function(id) { if (this.uploads[id]) { return this.uploads[id]; } return false; }, abort: function(id) { if (this.uploads[id]) { return this.uploads[id]['object'].abort(); } return false; } }); }]).directive('type', ['$parse', function urModelFileFactory($parse) { /** * Binding for file input elements */ return { scope: false, priority: 1, require: "?ngModel", link: function urFilePostLink(scope, element, attrs, ngModel) { if (attrs.type.toLowerCase() !== 'file' || !ngModel) { return; } element.bind('change', function(e) { if (!e.target.files || !e.target.files.length || !e.target.files[0]) { return true; } var index, fileData = attrs.multiple ? e.target.files : e.target.files[0]; ngModel.$render = function() {}; scope.$apply(function(scope) { index = scope.$index; $parse(attrs.ngModel).assign(scope, fileData); }); scope.$index = index; // @todo Make sure this can be replaced by ngChange. // For that to work, this event handler must have a higher priority than the one // defined by ngChange attrs.change ? scope.$eval(attrs.change) : null; }); } }; }]).directive('dropTarget', ['$parse', 'fileHandler', function urDropTargetFactory($parse, fileHandler) { return { scope: false, restrict: "EAC", require: "?ngModel", link: function urDropTargetLink(scope, element, attrs, ngModel) { var multiple = attrs.multiple, dropExpr = attrs.drop ? $parse(attrs.drop) : null, modelExpr = attrs.ngModel ? $parse(attrs.ngModel) : null; if (ngModel) ngModel.$render = function() {}; function stop(e) { e.stopPropagation(); e.preventDefault(); } var toIgnore = [], isOver = false; element.bind("dragenter", function dragEnter(e) { stop(e); if (e.target === this && !isOver) { if (attrs.overClass) element.addClass(attrs.overClass); isOver = true; return; } toIgnore.push(e.target); }); element.bind("dragleave", function dragExit(e) { stop(e); if (toIgnore.length === 0 && isOver) { if (attrs.overClass) element.removeClass(attrs.overClass); isOver = false; return; } toIgnore.pop(); }); element.bind("dragover", function(e) { stop(e); }); element.bind("drop", function(e) { stop(e); if (attrs.overClass) element.removeClass(attrs.overClass); isOver = false; e = e.originalEvent || e; var files = e.dataTransfer.files; if (!files.length) return; files = multiple ? files : files[0]; if (modelExpr) modelExpr.assign(scope, files); if (!dropExpr) return (scope.$$phase) ? null : scope.$apply(); var local = { $event: e }; local['$file' + (multiple ? 's' : '')] = files; var result = function() { dropExpr(scope, local); }; (scope.$$phase) ? result() : scope.$apply(result); }); } }; }]); ================================================ FILE: lib/public/js/app.js ================================================ var FMApp = angular.module('FMApp', ['ur.file']); FMApp.controller('FileManagerCtr', ['$scope', '$http', '$location', function ($scope, $http, $location) { var FM = this; FM.curHashPath = '#/'; // hash in browser url FM.curFolderPath = '/'; // current relative folder path FM.curBreadCrumbPaths = []; // items in breadcrumb list, for each level folder FM.curFiles = []; // files in current folder FM.selecteAll = false; // if select all files FM.selection = []; // selected files FM.renameName = ''; // new name for rename action FM.uploadFile = null; // will upload file FM.newFolderName = ''; FM.successData = '__init__'; FM.errorData = '__init__'; FM.updateArchiveName = function() { FM.archiveTarget = 'files_' + FM.curFolderPath.substring(1).replace(/\//g, '_') + new Date().toISOString().replace(/:/g, '.') + '.zip'; }; var hash2paths = function (relPath) { var paths = []; var names = relPath.split('/'); var path = '#/'; paths.push({name: 'Home', path: path}); for (var i=0; i File Manager
Type Name Size Time
{{file.name}} {{file.humanSize}} {{file.humanTime}}
================================================ FILE: package.json ================================================ { "name": "node-file-manager", "version": "0.4.6", "description": "File manager web based on Koa and Angular.js", "main": "index.js", "scripts": { "start": "node --harmony ./lib/index.js" }, "bin": { "node-file-manager": "../node-file-manager/bin/node-file-manager.sh" }, "dependencies": { "co": "^4.6.0", "co-busboy": "1.3.1", "co-from-stream": "0.0.0", "co-fs": "^1.2.0", "co-fs-extra": "^1.1.0", "co-views": "^2.1.0", "ejs": "^2.3.4", "jszip": "^3.1.3", "koa": "^1.0.0", "koa-bodyparser": "^2.0.1", "koa-morgan": "^0.3.0", "koa-mount": "^1.3.0", "koa-router": "^5.1.2", "koa-static": "^1.4.9", "optimist": "^0.6.1", "tracer": "^0.8.0" }, "repository": { "type": "git", "url": "git+https://github.com/efeiefei/node-file-manager.git" }, "keywords": [ "files", "manager", "koa", "angular", "web", "server" ], "author": { "name": "efei", "email": "efeigm@gmail.com" }, "license": "MIT", "bugs": { "url": "https://github.com/efeiefei/node-file-manager/issues" }, "homepage": "https://github.com/efeiefei/node-file-manager#readme" }