Skip to content

Commit

Permalink
Merge pull request #28 from VeliovGroup/dev_1
Browse files Browse the repository at this point in the history
v1.3.4
 - Fix minor bugs
 - Add `onBeforeUpload` for server side (see docs)
 - Faster code
  • Loading branch information
dr-dimitru committed Jul 24, 2015
2 parents b219c0a + f7c74c6 commit 2945edb
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 91 deletions.
2 changes: 1 addition & 1 deletion .versions
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ [email protected]
[email protected]
[email protected]
ostrio:[email protected]
ostrio:[email protected].3
ostrio:[email protected].4
ostrio:[email protected]
[email protected]
[email protected]
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ API
* __Note:__ Collection can not be `public` and `protected` at the same time!
* __Note:__ `integrityCheck` is __not__ guaranteed!
* __Note:__ `play` and force `download` features is __not__ guaranteed!
- `onBeforeUpload` {*Function*} - Callback triggered right before upload on __client__ and right after recieving a chunk on __server__, with __no arguments__:
* Context of the function is {*Object*}, with:
- `extension`, alias `ext`
- `mime-type`, alias `type`
- `size`
* __return__ `true` to continue
* __return__ `false` to abort or {*String*} to abort upload with message
- `onbeforeunloadMessage` {*String* or *Function*} - Message shown to user when closing browser's window or tab, while upload in the progress
- `allowClientCode` {*Boolean*} - Allow to run `remove()` from client
- `debug` {*Boolean*} - Turn on/of debugging and extra logging
Expand Down Expand Up @@ -345,7 +352,7 @@ Methods
- `onBeforeUpload` {*Function*} - Callback triggered right before upload is started, with __no arguments__:
* Context of the function is `File` - so you are able to check for extension, mime-type, size and etc.
* __return__ `true` to continue
* __return__ `false` to abort upload
* __return__ `false` to abort or {*String*} to abort upload with message
- `streams` {*Number*} - Quantity of parallel upload streams

Returns {*Object*}, with properties:
Expand Down Expand Up @@ -485,7 +492,7 @@ uploads.write buffer
,
(err, fileObj) ->
# Download File
window.open uploads.link(fileObj, 'original'), '_parent'
window.open uploads.link(fileObj, 'original')+'?download=true', '_parent'
```

###### `load(url, options, callback)` [*Server*]
Expand All @@ -509,5 +516,5 @@ uploads.load 'http://domain.com/small.png'
,
(err, fileObj) ->
# Download File
window.open uploads.link(fileObj, 'original'), '_parent'
window.open uploads.link(fileObj, 'original')+'?download=true', '_parent'
```
160 changes: 74 additions & 86 deletions files.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ cp = (to, from) ->
###
class Meteor.Files
constructor: (config) ->
{@storagePath, @collectionName, @downloadRoute, @schema, @chunkSize, @namingFunction, @debug, @onbeforeunloadMessage, @permissions, @allowClientCode, @integrityCheck, @protected, @public, @strict} = config if config
{@storagePath, @collectionName, @downloadRoute, @schema, @chunkSize, @namingFunction, @debug, @onbeforeunloadMessage, @permissions, @allowClientCode, @onBeforeUpload, @integrityCheck, @protected, @public, @strict} = config if config

@collectionName = 'MeteorUploadFiles' if not @collectionName
@chunkSize = 272144 if not @chunkSize
Expand All @@ -88,6 +88,7 @@ class Meteor.Files
@protected = false if not @protected
@public = false if not @public
@strict = true if not @strict
@onBeforeUpload = false if not @onBeforeUpload
@onbeforeunloadMessage = 'Upload in a progress... Do you want to abort?' if not @onbeforeunloadMessage

if @protected and Meteor.isClient
Expand Down Expand Up @@ -168,7 +169,6 @@ class Meteor.Files
@search = {}
@cacheControl = 'public, max-age=31536000'


@collection.attachSchema @schema

@collection.deny
Expand Down Expand Up @@ -273,10 +273,16 @@ class Meteor.Files
check partsQty, Number
check fileSize, Number

@unblock()
console.info "Meteor.Files Debugger: [MeteorFileWrite] {name: #{randFileName}, meta:#{meta}}" if self.debug
console.info "Meteor.Files Debugger: Received chunk ##{currentChunk} of #{chunksQty} chunks, in part: #{part}, file: #{fileData.name or fileData.fileName}" if self.debug

if self.onBeforeUpload and _.isFunction self.onBeforeUpload
isUploadAllowed = self.onBeforeUpload.call fileData
if isUploadAllowed isnt true
throw new Meteor.Error(403, if _.isString(isUploadAllowed) then isUploadAllowed else "@onBeforeUpload() returned false")

@unblock()

i = 0
binary = ''
while i < unitArray.byteLength
Expand All @@ -288,36 +294,21 @@ class Meteor.Files
str.replace(/\.\./g, '').replace /\//g, ''

fileName = cleanName(fileData.name or fileData.fileName)
ext = fileName.split('.').pop()
_newId = String.rand 32, 'ABCDEFabcdef' if self.public
pathName = if self.public then "#{self.storagePath}/original-#{_newId}" else "#{self.storagePath}/#{randFileName}"
path = if self.public then "#{self.storagePath}/original-#{_newId}.#{ext}" else "#{self.storagePath}/#{randFileName}.#{ext}"
pathPart = if partsQty > 1 then "#{pathName}_#{part}.#{ext}" else path
extension = fileName.split('.').pop()
pathName = if self.public then "#{self.storagePath}/original-#{randFileName}" else "#{self.storagePath}/#{randFileName}"
path = if self.public then "#{self.storagePath}/original-#{randFileName}.#{extension}" else "#{self.storagePath}/#{randFileName}.#{extension}"
pathPart = if partsQty > 1 then "#{pathName}_#{part}.#{extension}" else path

result =
result = self.dataToSchema
name: fileName
extension: ext
extension: extension
path: path
meta: meta
type: fileData.type
size: fileData.size
chunk: currentChunk
versions:
original:
path: path
size: fileData.size
type: fileData.type
extension: ext
last: last
isVideo: fileData.type.toLowerCase().indexOf("video") > -1
isAudio: fileData.type.toLowerCase().indexOf("audio") > -1
isImage: fileData.type.toLowerCase().indexOf("image") > -1
_prefix: self._prefix
_collectionName: self.collectionName
_storagePath: self.storagePath
_downloadRoute: self.downloadRoute

result._id = _newId if self.public

result.chunk = currentChunk
result.last = last

if first
fs.outputFileSync pathPart, binary, 'binary'
Expand All @@ -331,19 +322,43 @@ class Meteor.Files
buffers = []
i = 2
while i <= partsQty
fs.appendFileSync pathName + '_1.' + ext, fs.readFileSync(pathName + '_' + i + '.' + ext), 'binary'
fs.unlink pathName + '_' + i + '.' + ext
fs.appendFileSync pathName + '_1.' + extension, fs.readFileSync(pathName + '_' + i + '.' + extension), 'binary'
fs.unlink pathName + '_' + i + '.' + extension
i++
fs.renameSync pathName + '_1.' + ext, path
fs.renameSync pathName + '_1.' + extension, path

fs.chmod path, self.permissions
result._id = randFileName if self.public
result._id = self.collection.insert _.clone result

console.info "Meteor.Files Debugger: The file #{fileName} (binary) was saved to #{path}" if self.debug
return result

Meteor.methods _methods

dataToSchema: (data) ->
return {
name: data.name
extension: data.extension
path: data.path
meta: data.meta
type: data.type
size: data.size
versions:
original:
path: data.path
size: data.size
type: data.type
extension: data.extension
isVideo: data.type.toLowerCase().indexOf("video") > -1
isAudio: data.type.toLowerCase().indexOf("audio") > -1
isImage: data.type.toLowerCase().indexOf("image") > -1
_prefix: data._prefix or @_prefix
_collectionName: data._collectionName or @collectionName
_storagePath: data._storagePath or @storagePath
_downloadRoute: data._downloadRoute or @downloadRoute
}

srch: (search) ->
if search and _.isString search
@search =
Expand All @@ -370,35 +385,22 @@ class Meteor.Files
check callback, Match.Optional Function

if @checkAccess()
randFileName = @namingFunction.call null, true
randFileName = if @public then String.rand 32, 'ABCDEFabcdef' else @namingFunction.call null, true
fileName = if opts.name or opts.fileName then opts.name or opts.fileName else randFileName
extension = fileName.split('.').pop()
path = "#{@storagePath}/#{randFileName}.#{extension}"
path = if @public then "#{@storagePath}/original-#{randFileName}.#{extension}" else "#{@storagePath}/#{randFileName}.#{extension}"

opts.type = 'application/*' if not opts.type
opts.meta = {} if not opts.meta
opts.size = buffer.length if not opts.size

result =
result = @dataToSchema
name: fileName
extension: extension
path: path
meta: opts.meta
type: opts.type
size: opts.size
isVideo: if opts.type then opts.type.toLowerCase().indexOf("video") > -1 else false
isAudio: if opts.type then opts.type.toLowerCase().indexOf("audio") > -1 else false
isImage: if opts.type then opts.type.toLowerCase().indexOf("image") > -1 else false
versions:
original:
path: path
type: opts.type
size: opts.size
extension: extension
_prefix: @_prefix
_collectionName: @collectionName
_storagePath: @storagePath
_downloadRoute: @downloadRoute

console.info "Meteor.Files Debugger: The file #{fileName} (binary) was added to #{@collectionName}" if @debug

Expand Down Expand Up @@ -429,36 +431,23 @@ class Meteor.Files

self = @
if @checkAccess()
randFileName = @namingFunction.call null, true
randFileName = if @public then String.rand 32, 'ABCDEFabcdef' else @namingFunction.call null, true
fileName = if opts.name or opts.fileName then opts.name or opts.fileName else randFileName
extension = fileName.split('.').pop()
path = "#{@storagePath}/#{randFileName}.#{extension}"
path = if @public then "#{@storagePath}/original-#{randFileName}.#{extension}" else "#{@storagePath}/#{randFileName}.#{extension}"
opts.meta = {} if not opts.meta

request.get(url).on('error', (error)->
throw new Meteor.Error 500, "Error on [load(#{url}, #{opts})]; Error:" + JSON.stringify error
).on('response', (response) ->
bound ->
result =
result = self.dataToSchema
name: fileName
extension: extension
path: path
meta: opts.meta
type: response.headers['content-type']
size: response.headers['content-length']
isVideo: response.headers['content-type'].toLowerCase().indexOf("video") > -1
isAudio: response.headers['content-type'].toLowerCase().indexOf("audio") > -1
isImage: response.headers['content-type'].toLowerCase().indexOf("image") > -1
versions:
original:
path: path
type: response.headers['content-type']
size: response.headers['content-length']
extension: extension
_prefix: self._prefix
_collectionName: self.collectionName
_storagePath: self.storagePath
_downloadRoute: self.downloadRoute

console.info "Meteor.Files Debugger: The file #{fileName} (binary) was loaded to #{@collectionName}" if @debug

Expand All @@ -480,7 +469,7 @@ class Meteor.Files
@description Add file from FS to Meteor.Files
@returns {Files} - Return this
###
addFile: if Meteor.isServer then (path, opts, callback) ->
addFile: if Meteor.isServer then (path, opts = {}, callback) ->
console.info "[addFile(#{path})]" if @debug

throw new Meteor.Error 403, "Can not run [addFile()] on public collection" if @public
Expand All @@ -501,26 +490,14 @@ class Meteor.Files
opts.meta = {} if not opts.meta
opts.size = fileSize if not opts.size

result =
name: fileName
extension: ext
path: path
meta: opts.meta
type: opts.type
size: opts.size
isVideo: opts.type.toLowerCase().indexOf("video") > -1
isAudio: opts.type.toLowerCase().indexOf("audio") > -1
isImage: opts.type.toLowerCase().indexOf("image") > -1
versions:
original:
path: path
type: opts.type
size: opts.size
extension: ext
_prefix: @_prefix
_collectionName: @collectionName
_storagePath: path.replace "/#{fileName}", ''
_downloadRoute: @downloadRoute
result = @dataToSchema
name: fileName
extension: ext
path: path
meta: opts.meta
type: opts.type
size: opts.size
_storagePath: path.replace "/#{fileName}", ''

result._id = @collection.insert _.clone result
console.info "The file #{fileName} (binary) was added to #{@collectionName}" if @debug
Expand Down Expand Up @@ -671,10 +648,11 @@ class Meteor.Files
type: file.type
name: file.name
ext: file.name.split('.').pop()
extension: file.name.split('.').pop()
extension: file.name.split('.').pop()
'mime-type': file.type

file = _.extend file, fileData
randFileName = @namingFunction.call null, true
randFileName = if @public then String.rand 32, 'ABCDEFabcdef' else @namingFunction.call null, true
partSize = Math.ceil file.size / streams
parts = []
uploaded = 0
Expand Down Expand Up @@ -702,12 +680,18 @@ class Meteor.Files
result.progress.set 0
onUploaded and onUploaded.call self, error, data

if onBeforeUpload
if onBeforeUpload and _.isFunction onBeforeUpload
isUploadAllowed = onBeforeUpload.call file
if isUploadAllowed isnt true
end new Meteor.Error(403, if _.isString(isUploadAllowed) then isUploadAllowed else "onBeforeUpload() returned false"), null
return false

if @onBeforeUpload and _.isFunction @onBeforeUpload
isUploadAllowed = @onBeforeUpload.call file
if isUploadAllowed isnt true
end new Meteor.Error(403, if _.isString(isUploadAllowed) then isUploadAllowed else "@onBeforeUpload() returned false"), null
return false

upload = (filePart, part, chunksQtyInPart, fileReader) ->
currentChunk = 1
first = true
Expand All @@ -726,10 +710,12 @@ class Meteor.Files

if chunksQtyInPart is 1
Meteor.call self.methodNames.MeteorFileWrite, unitArray, fileData, meta, first, chunksQtyInPart, currentChunk, totalSentChunks, randFileName, part, streams, file.size, (error, data) ->
return end error if error
if data.last
end error, data
else
Meteor.call self.methodNames.MeteorFileWrite, unitArray, fileData, meta, first, chunksQtyInPart, currentChunk, totalSentChunks, randFileName, part, streams, file.size, (error, data)->
return end error if error
if not result.onPause.get()
if data.chunk + 1 <= chunksQtyInPart
from = currentChunk * self.chunkSize
Expand Down Expand Up @@ -758,6 +744,8 @@ class Meteor.Files
upload.call null, file.slice(part.from, part.to), i + 1, part.chunksQty, fileReader

return result
else
console.warn "Meteor.Files: [insert({file: 'file', ..})]: file property is required"
else
undefined

Expand Down
2 changes: 1 addition & 1 deletion package.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package.describe({
name: 'ostrio:files',
version: '1.3.3',
version: '1.3.4',
summary: 'Upload, Store and Stream (Video & Audio streaming) files to/from file system (FS) via DDP and HTTP',
git: 'https://github.com/VeliovGroup/Meteor-Files',
documentation: 'README.md'
Expand Down

0 comments on commit 2945edb

Please sign in to comment.