- Overview
- Why this package?
- Install
- API
- new Meteor.Files() [Isomorphic]
- Chunk Streaming [Example]
- Force Download [Example]
- Schema [Isomorphic]
- Template Helper
- Force Download [Example]
- Get file subversion [Example]
- Thumbnail Example [Example]
- Video Streaming Example [Example]
- General Methods
- Insert (Upload) File(s) [Client] - Upload file to server
- Collection [Isomorphic]
- findOne() [Isomorphic]
- find() [Isomorphic]
- write() [Server] - Write binary code into FS
- load() [Server] - Upload file from remote host
This package allows to:
- Upload / Read files in Cordova app: Cordva support (Any with support of
FileReader
) - Upload file(s) via DDP
- Small files
- Huge files, tested on 100GB (Note Browser will eat 7%-10% RAM of the file size)
- Pause / Resume upload
- Auto-pause when connection to server is interrupted
- Multi-stream async upload (faster than ever)
- Serving files
- Set
public
option totrue
to serve files via your proxy server, like nginx - Set
protected
option totrue
to serve files only to authorized users, or tofunction()
to check user's permission - Files CRC check (integrity check)
- Set
- Write file in file system
- Automatically writes uploaded files on FS and special Collection
- You able to specify
path
, collection name, schema, chunk size and naming function - Store multiply versions of the file, like revisions, image thumbnails or video in different formats, and etc.
- File streaming from server via HTTP
- Correct
mime-type
andContent-Range
headers - Correct
206
and416
responses
- Correct
- Download uploaded files from server via HTTP
- You able to specify
route
- Download huge files via stream
pipe
, tested on 100GB - Fast download for small files
- You able to specify
- Store wherever you like
- You may use
Meteor-Files
as temporary storage - After file is uploaded and stored on FS you able to
mv
orcp
it's content
- You may use
- Support of non-latin (non-Roman) file names
- Subscribe on files you need
cfs
is a good package, but buggy cause it's huge monster which combine everything. In Meteor-Files
is nothing to broke, it's simply upload/store/retrieve files to/from server.
- You need store to GridFS? - Add it yourself
- You need to check file mime-type or extensions? - Add it yourself
- You need to resize images after upload? - Add it yourself
Easy-peasy kids, yeah?
meteor add ostrio:files
config
is optional object with next properties:
storagePath
{String} - Storage path on file system- Default value:
/assets/app/uploads
- Default value:
collectionName
{String} - Collection name- Default value:
MeteorUploadFiles
- Default value:
cacheControl
{String} - DefaultCache-Control
header, by default:public, max-age=31536000, s-maxage=31536000
downloadRoute
{String} - Server Route used to retrieve files- Default value:
/cdn/storage
- Default value:
schema
{Object} - Collection Schema (Not editable for current release)chunkSize
{Number} - Upload chunk size- Default value:
272144
- Default value:
namingFunction
{Function} - Function which returnsString
- Default value:
String.rand
- Default value:
permissions
{Number} - FS-permissions (access rights) in octal, like0755
or0777
- Default value:
0777
- Default value:
integrityCheck
{Boolean} - CRC file check- Default value:
true
- Default value:
strict
{Boolean} - Strict mode for partial content, if istrue
server will return416
response code, whenrange
is not specified- Default value:
false
- Default value:
downloadCallback
{Function} - Called right before initiate file download, with next context and only one argumentfileObj
:fileObj
- see Current schema section below- context:
@request
@response
@user()
@userId
- Notes:
- Function should return {Boolean} value, to abort download - return
false
, to allow download - returntrue
- Function should return {Boolean} value, to abort download - return
protected
{Boolean|Function} - Iftrue
- files will be served only to authorized users, iffunction()
- you're able to check visitor's permissions in your own way- Default value:
false
- If function -
function
context has:@request
- On server only@response
- On server only@user()
@userId
- Default value:
public
{Boolean} - Iftrue
- files will be stored in folder publicity available for proxy servers, like nginx- Default value:
false
- Route:
http://example.com/uploads/:collectionName/:fileName
- Note: Collection can not be
public
andprotected
at the same time! - Note:
integrityCheck
is not guaranteed! - Note:
play
and forcedownload
features is not guaranteed!
- Default value:
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
, aliasext
mime-type
, aliastype
size
- return
true
to continue - return
false
to abort or {String} to abort upload with message
- Context of the function is {Object}, with:
onbeforeunloadMessage
{String or Function} - Message shown to user when closing browser's window or tab, while upload in the progress- Default value:
'Upload in a progress... Do you want to abort?'
- Default value:
allowClientCode
{Boolean} - Allow to runremove()
from clientdebug
{Boolean} - Turn on/of debugging and extra logging- Default value:
false
- Default value:
myFiles.cacheControl = 'public, max-age=31536000' # Set 'Cache-Control' header for downloads
myFiles = new Meteor.Files
storagePath: 'assets/app/uploads/myFiles'
collectionName: 'myFiles'
chunkSize: 256*128
permissions: 0o777
allowClientCode: true
onbeforeunloadMessage: ->
i18n.get '_app.abortUpload' # See 'ostrio:i18n' package
fileObject = myFiles.findOne 'recordId'
if Meteor.isClient
myFiles.collection.subscribe "MeteorFileSubs", postId.get()
if Meteor.isServer
Meteor.publish "MeteorFileSubs", (postId) ->
myFiles.collection.find {'meta.postId': postId}
myFiles.insert(file) # Upload file
myFiles.find({'meta.userId': Meteor.userId()}).cursor # Current collection cursor
myFiles.find({'meta.userId': Meteor.userId()}).get() # Array of fetched rows
myFiles.find({'meta.userId': Meteor.userId()}).remove() # Remove all files on the cursor
myFiles.remove({'meta.userId': Meteor.userId()}) # Remove all files returned by passed search
myFiles.remove(fileRef._id) # Remove file by ID
myFiles.findOne(fileRef._id).get() # Get fileRef
myFiles.findOne(fileRef._id).link() # Get download link
myFiles.findOne(fileRef._id).link('version') # Get download link of specific version
myFiles.link(fileObject) # Get download link
myFiles.link(fileObject, 'version') # Get download link of specific version
myFiles.findOne(fileRef._id).remove() # Remove file
To stream file add ?play=true
query to download link.
audio = new Meteor.Files()
if Meteor.isClient
Template.my.helpers
audiofiles: ->
audio.find({'meta.post': postId}).cursor
In template:
template(name="my")
ul
each audiofiles
li
audio(preload="auto" controls="true")
source(src="{{fileURL this}}?play=true" type="{{type}}")
To download file use fileURL
template helper. Data will be transfered via pipe, - just add ?download=true
query to link, so server will send file directly. Use ?download=true
query for smaller files, for big files - just use plain link without query.
uploads = new Meteor.Files()
if Meteor.isClient
Template.my.helpers
files: ->
uploads.find({'meta.post': postId}).cursor
In template:
template(name="my")
ul
each files
li
a(href="{{fileURL this}}?download=true" target="_parent") name
name:
type: String
type:
type: String
extension:
type: String
path:
type: String
meta:
type: Object
blackbox: true
optional: true
versions:
type: Object
blackbox: true
###
Example
original:
path: String
size: Number
type: String
extension: String
...
other:
path: String
size: Number
type: String
extension: String
###
userId:
type: String
optional: true
isVideo:
type: Boolean
isAudio:
type: Boolean
isImage:
type: Boolean
size:
type: Number
a(href="{{fileURL fileRef}}?download=true" target="_parent" download) {{fileRef.name}}
Note: If requested version of file is not available - the original file will be returned
a(href="{{fileURL fileRef 'small'}}?download=true" target="_parent" download) {{fileRef.name}}
Note: If thumbnail (basically version of the file) is not available the original file will be returned
img(src="{{fileURL fileRef 'thumb'}}" alt="{{fileRef.name}}")
video(width="80%" height="auto" controls="controls" poster="{{fileURL fileRef 'videoPoster'}}")
source(src="{{fileURL fileRef 'ogg'}}?play=true" type="video/ogg")
source(src="{{fileURL fileRef 'mp4'}}?play=true" type="video/mp4")
source(src="{{fileURL fileRef 'webm'}}?play=true" type="video/webm")
Note!: There is no build-in way for image or video resizing, encoding and re-sampling, below example how you can multiple file versions:
FilesCollection = new Meteor.Files()
if Meteor.isClient
'change #upload': (e, template) ->
_.each e.currentTarget.files, (file) ->
Collections.FilesCollection.insert
file: file
onUploaded: (error, fileObj) ->
if error
alert error.message
throw Meteor.log.warn "File Upload Error", error
template.$(e.target).val('')
template.$(e.currentTarget).val('')
Meteor.call 'convertVideo', fileObj, () ->
alert "File \"#{fileObj.name}\" successfully uploaded"
onProgress: _.throttle (progress) ->
template.$('input#progress').val progress
,
500
onBeforeUpload: () ->
if ['ogg', 'mp4', 'avi', 'webm'].inArray(@ext) and @size < 512 * 1048 * 1048
true
else
"Please upload file in next formats: 'ogg', 'mp4', 'avi', 'webm' with size less than 512 Mb. You have tried to upload file with \"#{@ext}\" extension and with \"#{Math.round((@size/(1024*1024)) * 100) / 100}\" Mb"
streams: 8
if Meteor.isServer
###
@var {object} bound - Meteor.bindEnvironment aka Fiber wrapper
###
bound = Meteor.bindEnvironment (callback) ->
return callback()
###
@description Require "fs-extra" npm package
###
fs = Npm.require "fs-extra"
Meteor.methods
convertVideo: (fileRef) ->
check fileRef, Object
sourceFile = ffmpeg(fileRef.path).noProfile()
formats =
ogg: true
mp4: true
webm: true
_.each formats, (convert, format) ->
file = _.clone sourceFile
bound ->
version = file.someHowConvertVideoAndReturnFileData(format)
upd =
$set: {}
upd['$set']['versions.' + name] =
path: version.path
size: version.size
type: version.type
extension: version.extension
FilesCollection.collection.update fileRef._id, upd
return true
settings
is required object with next properties:
file
{File} or {Object} - [REQUIRED] HTML5files
item, like in change event:event.currentTarget.files[0]
meta
{Object} - Additional data as object, use later for searchonUploaded
{Function} - Callback triggered when upload is finished, with two arguments:error
fileRef
- see Current schema section above
onProgress
{Function} - Callback triggered when chunk is sent, with only argument:- {
progress
Number} - Current progress from0
to100
- {
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 or {String} to abort upload with message
- Context of the function is
streams
{Number} - Quantity of parallel upload streamsonAbort
{Function} - Callback triggered whenabort()
method is called, with only one argument:file
- File object passed asfile
property toinsert()
method
Returns {Object}, with properties:
onPause
{ReactiveVar} - Is upload process on the pause?progress
{ReactiveVar} - Upload progress in percentspause
{Function} - Pause upload processcontinue
{Function} - Continue paused upload processtoggleUpload
{Function} - Togglecontinue
/pause
if upload in the progressabort
{Function} - Abort upload progress, and triggeronAbort
callback
# For example we upload file for blog post
post = Posts.findOne(someId) # Get blog post reference
uploads = new Meteor.Files() # Create Meteor.Files instance
if Meteor is client
# Let's create progress-bar
currentUploadProgress = new ReactiveVar false
Template.my.helpers
progress: ->
currentUploadProgress.get()
Template.my.events
'change #file': (e) ->
_.each e.currentTarget.files, (file) ->
uploads.insert
file: file
meta:
post: post._id # Add meta object with reference to blog post
onUploaded: (error, fileObj) ->
if not error
doSomething fileRef.path, post._id, fileRef
currentUploadProgress.set false
$(e.target).val('')
onProgress: _.throttle (progress) ->
currentUploadProgress.set progress
,
500
onBeforeUpload: () ->
# Set Allowed Extensions and max file size
allowedExt = ['mp3', 'm4a']
allowedMaxSize = 26214400
if allowedExt.inArray(@ext) and @size < allowedMaxSize # See `ostrio:jsextensions` package
true
else
"Max upload size is #{Math.round((allowedMaxSize/(1024*1024)) * 100) / 100} Mb. Allowed extensions is #{allowedExt.join(', ')}"
streams: 8
Progress bar in template (TWBS):
template(name="my")
input.btn.btn-success#file(type="file")
if progress
.progress
.progress-bar.progress-bar-striped.active(style="width:{{progress}}%")
span.sr-only {{progress}}%
Mongo Collection Instance - Use to fetch data. Do not remove
or update
this collection
uploads = new Meteor.Files()
if Meteor.isClient
# postId.get() is some ReactiveVar or session
Meteor.subscribe "MeteorFileSubs", postId.get()
if Meteor.isServer
Meteor.publish "MeteorFileSubs", (postId) ->
uploads.collection.find {'meta.postId': postId}
uploads.collection.find({'meta.post': post._id})
uploads.collection.findOne('hdjJDSHW6784kJS')
Find one fileObj matched by passed search
search
{String or Object} -_id
of the file orObject
uploads = new Meteor.Files()
uploads.findOne('hdjJDSHW6784kJS').get() # Get fileRef
uploads.findOne('hdjJDSHW6784kJS').remove() # Remove file
uploads.findOne('hdjJDSHW6784kJS').link('version') # Get download link
uploads.findOne({'meta.post': post._id}).get() # Get fileRef
uploads.findOne({'meta.post': post._id}).remove() # Remove file
uploads.findOne({'meta.post': post._id}).link() # Get download link
Find multiply fileObj matched by passed search
search
{String or Object} -_id
of the file orObject
uploads = new Meteor.Files()
uploads.find({'meta.post': post._id}).cursor # Current cursor
uploads.find({'meta.post': post._id}).fetch() # Get cursor as Array (Array of objects)
uploads.find('hdjJDSHW6784kJS').get() # Get array of fileRef(s)
uploads.find({'meta.post': post._id}).get() # Get array of fileRef(s)
uploads.find({'meta.post': post._id}).remove() # Remove all files on cursor
Write binary data to FS and store in Meteor.Files
collection
buffer
{Buffer} - Binary dataoptions
{Object} - Object with next properties:type
- File mime-typesize
- File sizemeta
- Additional data as object, use later for searchname
orfileName
- File name
callback(error, fileObj)
Returns:
fileObj
{Object}
uploads = new Meteor.Files()
buffer = fs.readFileSync 'path/to/file.jpg'
uploads.write buffer
,
type: 'image/jpeg'
name: 'MyImage.jpg'
meta:
post: post._id
,
(err, fileObj) ->
# Download File
window.open uploads.link(fileObj, 'original')+'?download=true', '_parent'
Upload file via http from remote host to server's FS and store in Meteor.Files
collection
url
{String} - Binary dataoptions
{Object} - Object with next properties:meta
- Additional data as object, use later for searchname
orfileName
- File name
callback(error, fileObj)
Returns:
fileObj
{Object}
uploads = new Meteor.Files()
uploads.load 'http://domain.com/small.png'
,
name: 'small.png'
meta:
post: post._id
,
(err, fileObj) ->
# Download File
window.open uploads.link(fileObj, 'original')+'?download=true', '_parent'