Skip to content

Commit 5e133bc

Browse files
author
pemrouz
committed
init commit
0 parents  commit 5e133bc

File tree

8 files changed

+393
-0
lines changed

8 files changed

+393
-0
lines changed

.babelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["es2015"]
3+
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
coverage
3+
*.log

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
sudo: false
2+
language: node_js
3+
node_js:
4+
- "stable"
5+
6+
after_script: NODE_ENV=test istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage

README.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Ripple | Upload
2+
[![Coverage Status](https://coveralls.io/repos/rijs/upload/badge.svg?branch=master&service=github)](https://coveralls.io/github/rijs/upload?branch=master)
3+
[![Build Status](https://travis-ci.org/rijs/upload.svg)](https://travis-ci.org/rijs/upload)
4+
5+
This module makes uploading forms nicer to deal with. In particlar, it turns `type="file"` elements into a native Stream (with backpressure and all) on the server-side so you can then simply pipe them.
6+
7+
First, use [utilise/form](https://github.com/utilise/utilise#--form) to turn your form element into a simple plain JSON object.
8+
9+
```js
10+
const { values } = form(formElement)
11+
```
12+
13+
Then upload. You get two events for updates: `progress` and `response`.
14+
15+
```js
16+
ripple.upload('resource', values)
17+
.on('progress', progress => ...)
18+
.on('progress', response => ...)
19+
```
20+
21+
The `resource` identifies which handler to use on the server-side. For example, let's say you registered an `events` resource, then its `from` function would receive the upload. Complete example:
22+
23+
```html
24+
<form>
25+
<input type="text" name="title">
26+
<input type="file" name="photos">
27+
</form>
28+
```
29+
30+
```js
31+
32+
// server
33+
ripple('events', [], { from })
34+
35+
function from(req, res){
36+
if (!req.type == 'upload') return
37+
38+
// req.value contains everything you need to process
39+
req.value == {
40+
name: ... // some text
41+
photos: [...] // An array of streams, one for each file
42+
}
43+
44+
// Use res to respond directly to the upload.
45+
// You can pass any arguments you like.
46+
// This is what the "response" event on the client receives
47+
if (success)
48+
res(200, '/event/123')
49+
else
50+
res(500, 'error')
51+
}
52+
53+
// client
54+
const { values } = form(formElement)
55+
56+
ripple.upload('events', values)
57+
.on('progress', progress => {
58+
swal({
59+
title: 'Uploading Event..'
60+
, content: progress + '%'
61+
, type: 'working'
62+
})
63+
})
64+
.on('response', (status, url) => {
65+
const title = status == 200 ? 'Done' : 'Error'
66+
, type = status == 200 ? 'success' : 'error'
67+
, content = status == 200
68+
? 'Great - Your event has now been published!'
69+
: 'Uh oh, something went wrong! Please try again later'
70+
, buttons = [{ type: 'primary', text: 'Close' }]
71+
72+
go(url)
73+
swal({ title, type, content, buttons })
74+
})
75+
```
76+
77+
(NB: `swal` comes from [pemrouz/sweet-alert](https://github.com/pemrouz/sweet-alert) and `go` from [pemrouz/decouter](https://github.com/pemrouz/decouter))

dist/index.js

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
'use strict';
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
exports.default = upload;
7+
8+
var _emitterify = require('utilise/emitterify');
9+
10+
var _emitterify2 = _interopRequireDefault(_emitterify);
11+
12+
var _values = require('utilise/values');
13+
14+
var _values2 = _interopRequireDefault(_values);
15+
16+
var _client = require('utilise/client');
17+
18+
var _client2 = _interopRequireDefault(_client);
19+
20+
var _keys = require('utilise/keys');
21+
22+
var _keys2 = _interopRequireDefault(_keys);
23+
24+
var _key = require('utilise/key');
25+
26+
var _key2 = _interopRequireDefault(_key);
27+
28+
var _to = require('utilise/to');
29+
30+
var _to2 = _interopRequireDefault(_to);
31+
32+
/* istanbul ignore next */
33+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
34+
35+
// -------------------------------------------
36+
// Streams Forms
37+
// -------------------------------------------
38+
function upload(ripple) {
39+
log('creating');
40+
41+
if (_client2.default) {
42+
ripple.upload = up(ripple), ripple;
43+
ripple.upload.log = [];
44+
} else {
45+
ripple.io.on('connection', connected(require('socket.io-stream')));
46+
}
47+
48+
return ripple;
49+
}
50+
51+
var connected = function connected(ss) {
52+
return function (socket) {
53+
socket.on('upload', function (meta, res) {
54+
var id = socket.ip + '-' + meta.time;
55+
log('uploading form', id);
56+
meta.res = res;
57+
buffer[id] = meta;
58+
if (!(0, _values2.default)(meta.files).some(Boolean)) end(socket, id);
59+
});
60+
61+
ss(socket).on('file', function (stream, data) {
62+
var filename = data.filename;
63+
var size = data.size;
64+
var name = data.name;
65+
var i = data.i;
66+
var time = data.time;
67+
var id = socket.ip + '-' + time;
68+
var _buffer$id = buffer[id];
69+
var files = _buffer$id.files;
70+
var fields = _buffer$id.fields;
71+
var resource = _buffer$id.resource;
72+
73+
74+
fields[name][i] = stream;
75+
if (fields[name].filter(Boolean).length == files[name]) delete files[name];
76+
if (!(0, _values2.default)(files).some(Boolean)) end(socket, id);
77+
});
78+
79+
var end = function end(socket, id) {
80+
var type = 'upload';
81+
var resource = buffer[id].resource;
82+
var value = buffer[id].fields;
83+
var time = buffer[id].time;
84+
var res = buffer[id].res;
85+
var from = (0, _key2.default)('resources.' + resource + '.headers.from')(ripple);
86+
var name = resource.name;
87+
88+
89+
log('finished', id, time);
90+
if (!from) return err('no handler for', resource);
91+
from({ name: name, type: type, value: value, socket: socket }, res);
92+
delete buffer[id];
93+
};
94+
};
95+
};
96+
97+
var up = function up(ripple) {
98+
return function (resource, data) {
99+
var files = {},
100+
fields = {},
101+
ret = (0, _emitterify2.default)({}),
102+
time = ripple.upload.log.push(ret) - 1,
103+
meta = { files: files, fields: fields, resource: resource, time: time },
104+
count = function count(d) {
105+
return totalSize += d.size;
106+
};
107+
108+
var totalSize = 0,
109+
uploadedSize = 0;
110+
111+
(0, _keys2.default)(data).map(function (name) {
112+
return data[name] instanceof FileList ? (files[name] = _to2.default.arr(data[name]).map(count).length, fields[name] = []) : fields[name] = data[name];
113+
});
114+
115+
log('uploading form', meta, totalSize);
116+
ripple.io.emit('upload', meta, done);
117+
118+
(0, _keys2.default)(files).map(function (name) {
119+
var i = files[name];
120+
while (i--) {
121+
var file = data[name][i],
122+
size = file.size,
123+
filename = file.name,
124+
stream = ss.createStream();
125+
126+
ss(ripple.io).emit('file', stream, { filename: filename, size: size, name: name, i: i, time: time });
127+
ss.createBlobReadStream(file).on('data', function (chunk) {
128+
uploadedSize += chunk.length;
129+
ret.emit('progress', ~~(uploadedSize / totalSize * 100));
130+
}).pipe(stream);
131+
}
132+
});
133+
134+
function done() {
135+
log('uploaded', time, arguments);
136+
ret.emit('response', arguments);
137+
delete ripple.upload.log[time];
138+
}
139+
140+
return ret;
141+
};
142+
};
143+
144+
var log = require('utilise/log')('[ri/upload]'),
145+
err = require('utilise/err')('[ri/upload]'),
146+
buffer = {};

package.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"name": "rijs.upload",
3+
"version": "0.0.0",
4+
"main": "dist",
5+
"author": "Pedram Emrouznejad (https://github.com/pemrouz)",
6+
"repository": {
7+
"type": "git",
8+
"url": "git://github.com/rijs/upload.git"
9+
},
10+
"scripts": {
11+
"ignore": "find ./dist -type f -exec sed -i -E \"s/(function _interopRequire)/\\/* istanbul ignore next *\\/\\n\\1/g\" {} ';'",
12+
"babel": "babel src -d dist",
13+
"clean": "rm -rf dist && mkdir dist",
14+
"build": "npm run clean && npm run babel && npm run ignore",
15+
"test": "istanbul test ./node_modules/mocha/bin/_mocha --report html -- -R spec --colors",
16+
"coverage": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && rm -rf ./coverage --colors",
17+
"cov": "istanbul cover ./node_modules/mocha/bin/_mocha -- -R spec --colors",
18+
"test-popper": "popper",
19+
"version": "npm run build && git add -A",
20+
"postversion": "git push && git push --tags"
21+
},
22+
"license": "pemrouz.mit-license.org",
23+
"devDependencies": {
24+
"babel-cli": "*",
25+
"babel-preset-es2015": "*",
26+
"browserify": "*",
27+
"browserenv": "*",
28+
"chai": "*",
29+
"coveralls": "*",
30+
"istanbul": "*",
31+
"mocha": "*",
32+
"mocha-lcov-reporter": "*",
33+
"mockery": "^1.4.0",
34+
"popper": "*",
35+
"rijs.components": "*",
36+
"rijs.core": "*",
37+
"rijs.css": "*",
38+
"rijs.data": "*",
39+
"rijs.fn": "*",
40+
"rijs.serve": "*",
41+
"rijs.shadow": "*",
42+
"rijs.sync": "*",
43+
"uglify-js": "*"
44+
},
45+
"dependencies": {
46+
"socket.io-stream": "^0.9.0",
47+
"utilise": "*"
48+
}
49+
}

0 commit comments

Comments
 (0)