Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nodejs sample with angularjs #260

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions samples/Node.js/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
process.env.TMPDIR = 'tmp'; // to avoid the EXDEV rename error, see http://stackoverflow.com/q/21071303/76173

var express = require('express');
var multipart = require('connect-multiparty');
var multipartMiddleware = multipart();
var app = express();
var flowroutes = require('./routes/flow-routes.js');

// Host most stuff in the public folder
app.use(express.static(__dirname + '/public'));
app.use('/dist', express.static(__dirname + '/../../dist'));
app.use('/bower_components', express.static(__dirname + '/../../bower_components'));

// Default Route
app.use('/',flowroutes);

app.listen(3000);
216 changes: 216 additions & 0 deletions samples/Node.js/models/flow-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
var fs = require('fs'),
path = require('path'),
util = require('util'),
Stream = require('stream').Stream;

module.exports = flow = function(temporaryFolder) {
var $ = this;
$.temporaryFolder = temporaryFolder;
$.maxFileSize = null;
$.fileParameterName = 'file';

try {
fs.mkdirSync($.temporaryFolder);
} catch (e) {}

function cleanIdentifier(identifier) {
return identifier.replace(/[^0-9A-Za-z_-]/g, '');
}

function getChunkFilename(chunkNumber, identifier) {
// Clean up the identifier
identifier = cleanIdentifier(identifier);
// What would the file name be?
return path.resolve($.temporaryFolder, './flow-' + identifier + '.' + chunkNumber);
}

function validateRequest(chunkNumber, chunkSize, totalSize, identifier, filename, fileSize) {
// Clean up the identifier
identifier = cleanIdentifier(identifier);

// Check if the request is sane
if (chunkNumber == 0 || chunkSize == 0 || totalSize == 0 || identifier.length == 0 || filename.length == 0) {
return 'non_flow_request';
}
var numberOfChunks = Math.max(Math.floor(totalSize / (chunkSize * 1.0)), 1);
if (chunkNumber > numberOfChunks) {
return 'invalid_flow_request1';
}

// Is the file too big?
if ($.maxFileSize && totalSize > $.maxFileSize) {
return 'invalid_flow_request2';
}

if (typeof(fileSize) != 'undefined') {
if (chunkNumber < numberOfChunks && fileSize != chunkSize) {
// The chunk in the POST request isn't the correct size
return 'invalid_flow_request3';
}
if (numberOfChunks > 1 && chunkNumber == numberOfChunks && fileSize != ((totalSize % chunkSize) + parseInt(chunkSize))) {
// The chunks in the POST is the last one, and the fil is not the correct size
return 'invalid_flow_request4';
}
if (numberOfChunks == 1 && fileSize != totalSize) {
// The file is only a single chunk, and the data size does not fit
return 'invalid_flow_request5';
}
}

return 'valid';
}

//'found', filename, original_filename, identifier
//'not_found', null, null, null
$.get = function(req, callback) {
var chunkNumber = req.params('flowChunkNumber', 0);
var chunkSize = req.params('flowChunkSize', 0);
var totalSize = req.params('flowTotalSize', 0);
var identifier = req.params('flowIdentifier', "");
var filename = req.params('flowFilename', "");

if (validateRequest(chunkNumber, chunkSize, totalSize, identifier, filename) == 'valid') {
var chunkFilename = getChunkFilename(chunkNumber, identifier);
fs.exists(chunkFilename, function(exists) {
if (exists) {
callback('found', chunkFilename, filename, identifier);
} else {
callback('not_found', null, null, null);
}
});
} else {
callback('not_found', null, null, null);
}
};

//'partly_done', filename, original_filename, identifier
//'done', filename, original_filename, identifier
//'invalid_flow_request', null, null, null
//'non_flow_request', null, null, null
$.post = function(req, callback) {

var fields = req.body;
var files = req.files;

var chunkNumber = fields['flowChunkNumber'];
var chunkSize = fields['flowChunkSize'];
var totalSize = fields['flowTotalSize'];
var identifier = cleanIdentifier(fields['flowIdentifier']);
var filename = fields['flowFilename'];

if (!files[$.fileParameterName] || !files[$.fileParameterName].size) {
callback('invalid_flow_request', null, null, null);
return;
}

var original_filename = files[$.fileParameterName]['originalFilename'];
var validation = validateRequest(chunkNumber, chunkSize, totalSize, identifier, filename, files[$.fileParameterName].size);
if (validation == 'valid') {
var chunkFilename = getChunkFilename(chunkNumber, identifier);
var tmpChunkFilename = files[$.fileParameterName].path;
// Save the chunk (TODO: OVERWRITE)
fs.rename(tmpChunkFilename, chunkFilename, function(err) {
// Log any error related to fs.rename (different partitions)
if (err){
console.log(err);
return;
}

// Do we have all the chunks?
var currentTestChunk = 1;
var numberOfChunks = Math.max(Math.floor(totalSize / (chunkSize * 1.0)), 1);

var testChunkExists = function() {
fs.exists(getChunkFilename(currentTestChunk, identifier), function(exists) {
if (exists) {
currentTestChunk++;
if (currentTestChunk > numberOfChunks) {
callback('done', filename, original_filename, identifier);
} else {
// Recursion
testChunkExists();
}
} else {
callback('partly_done', filename, original_filename, identifier);
}
});
};
testChunkExists();
});
} else {
callback(validation, filename, original_filename, identifier);
}
};

// Pipe chunks directly in to an existsing WritableStream
// r.write(identifier, response);
// r.write(identifier, response, {end:false});
//
// var stream = fs.createWriteStream(filename);
// r.write(identifier, stream);
// stream.on('data', function(data){...});
// stream.on('finish', function(){...});
$.write = function(identifier, writableStream, options) {
options = options || {};
options.end = (typeof options['end'] == 'undefined' ? true : options['end']);

// Iterate over each chunk
var pipeChunk = function(number) {

var chunkFilename = getChunkFilename(number, identifier);
fs.exists(chunkFilename, function(exists) {

if (exists) {
// If the chunk with the current number exists,
// then create a ReadStream from the file
// and pipe it to the specified writableStream.
var sourceStream = fs.createReadStream(chunkFilename);
sourceStream.pipe(writableStream, {
end: false
});
sourceStream.on('end', function() {
// When the chunk is fully streamed,
// jump to the next one
pipeChunk(number + 1);
});
} else {
// When all the chunks have been piped, end the stream
if (options.end) writableStream.end();
if (options.onDone) options.onDone();
}
});
};
pipeChunk(1);
};

$.clean = function(identifier, options) {
options = options || {};

// Iterate over each chunk
var pipeChunkRm = function(number) {

var chunkFilename = getChunkFilename(number, identifier);

//console.log('removing pipeChunkRm ', number, 'chunkFilename', chunkFilename);
fs.exists(chunkFilename, function(exists) {
if (exists) {

console.log('exist removing ', chunkFilename);
fs.unlink(chunkFilename, function(err) {
if (err && options.onError) options.onError(err);
});

pipeChunkRm(number + 1);

} else {

if (options.onDone) options.onDone();

}
});
};
pipeChunkRm(1);
};

return $;
};
6 changes: 6 additions & 0 deletions samples/Node.js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"dependencies": {
"express": "^4.3.1",
"connect-multiparty": "^1.0.4"
}
}
104 changes: 104 additions & 0 deletions samples/Node.js/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<!DOCTYPE html>
<html ng-app="app" flow-init>
<head>
<title>basic</title>
<script src="/bower_components/angular/angular.js"></script>
<script src="/dist/ng-flow-standalone.js"></script>
<script src="/js/ngflow/app.js"></script>

<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css"
rel="stylesheet"/>
</head>
<body flow-prevent-drop
flow-drag-enter="style={border: '5px solid green'}"
flow-drag-leave="style={}"
ng-style="style">
<div class="container">
<h1>flow basic example</h1>
<hr class="soften"/>

<div class="row">
<div class="span6">
<h2>Inputs:</h2>

<input type="file" flow-btn/>
<input type="file" flow-btn flow-directory ng-show="$flow.supportDirectory"/>
</div>
<div class="span6">
<h2>Buttons:</h2>

<span class="btn" flow-btn><i class="icon icon-file"></i>Upload File</span>
<span class="btn" flow-btn flow-directory ng-show="$flow.supportDirectory"><i class="icon icon-folder-open"></i>
Upload Folder
</span>
</div>
</div>
<hr class="soften">

<h2>Transfers:</h2>

<p>
<a class="btn btn-small btn-success" ng-click="$flow.resume()">Upload</a>
<a class="btn btn-small btn-danger" ng-click="$flow.pause()">Pause</a>
<a class="btn btn-small btn-info" ng-click="$flow.cancel()">Cancel</a>
<span class="label label-info">Size: {{$flow.getSize()}}</span>
<span class="label label-info">Is Uploading: {{$flow.isUploading()}}</span>
</p>
<table class="table table-hover table-bordered table-striped" flow-transfers>
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Size</th>
<th>Relative Path</th>
<th>Unique Identifier</th>
<th>#Chunks</th>
<th>Progress</th>
<th>Paused</th>
<th>Uploading</th>
<th>Completed</th>
<th>Settings</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="file in transfers">
<td>{{$index+1}}</td>
<td>{{file.name}}</td>
<td>{{file.size}}</td>
<td>{{file.relativePath}}</td>
<td>{{file.uniqueIdentifier}}</td>
<td>{{file.chunks.length}}</td>
<td>{{file.progress()}}</td>
<td>{{file.paused}}</td>
<td>{{file.isUploading()}}</td>
<td>{{file.isComplete()}}</td>
<td>
<div class="btn-group">
<a class="btn btn-mini btn-warning" ng-click="file.pause()" ng-hide="file.paused">
Pause
</a>
<a class="btn btn-mini btn-warning" ng-click="file.resume()" ng-show="file.paused">
Resume
</a>
<a class="btn btn-mini btn-danger" ng-click="file.cancel()">
Cancel
</a>
<a class="btn btn-mini btn-info" ng-click="file.retry()" ng-show="file.error">
Retry
</a>
</div>
</td>
</tr>
</tbody>
</table>

<hr class="soften"/>

<div class="alert" flow-drop flow-drag-enter="class='alert-success'" flow-drag-leave="class=''"
ng-class="class">
Drag And Drop your file here
</div>
</div>
</body>
</html>

24 changes: 24 additions & 0 deletions samples/Node.js/public/js/ngflow/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*global angular */
'use strict';

/**
* The main app module
* @name app
* @type {angular.Module}
*/
var app = angular.module('app', ['flow'])
.config(['flowFactoryProvider', function (flowFactoryProvider) {
flowFactoryProvider.defaults = {
target: '/upload',
permanentErrors: [404, 500, 501],
testChunks: false,
maxChunkRetries: 1,
chunkRetryInterval: 5000,
simultaneousUploads: 4
};
flowFactoryProvider.on('catchAll', function (event) {
console.log('catchAll', arguments);
});
// Can be used with different implementations of Flow.js
// flowFactoryProvider.factory = fustyFlowFactory;
}]);
Loading