Giter VIP home page Giter VIP logo

meteor-files's Introduction

Meteor-Files

This package allows to:

  • 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)
  • 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
  • File streaming from server via HTTP
    • Correct mime-type and Content-Range headers
    • Correct 206 and 416 responses
  • 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
  • Store wherever you like
    • You may use Meteor-Files as temporary storage
    • After file is uploaded and stored on FS you able to mv or cp it's content
  • Support of non-latin (non-Roman) file names
  • Subscribe on files you need

Why Meteor-Files?

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?

Install:

meteor add ostrio:files

API

new Meteor.Files([config]) [{Isomorphic}]

config is optional object with next properties:

  • storagePath {String} - Storage path on file system
    • Default value: /assets/app/uploads
  • collectionName {String} - Collection name
    • Default value: MeteorUploadFiles
  • downloadRoute {String} - Server Route used to retrieve files
    • Default value: /cdn/storage
  • schema {Object} - Collection Schema (Not editable for current release)
  • chunkSize {Number} - Upload chunk size
    • Default value: 272144
  • namingFunction {Function} - Function which returns String
    • Default value: String.rand
  • permissions {Number} - Permissions or access rights in octal, like 0755 or 0777
  • 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
    • Default value: false
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
File streaming:

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}}")
File download:

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
Current schema:
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

Template Helper

To get download URL for file, you only need fileRef object, so there is no need for subscription:
a(href="{{fileURL fileRef}}?download=true" target="_parent" download) {{fileRef.name}}
To get specific version of the file use second argument version:

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}}
To display thumbnail:

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}}")
To stream video:
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

Methods

insert(settings) [Client]

settings is required object with next properties:

  • file {File} or {Object} - [REQUIRED] HTML5 files item, like in change event: event.currentTarget.files[0]
  • meta {Object} - Additional data as object, use later for search
  • onUploaded {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 from 0 to 100
  • 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
  • streams {Number} - Quantity of parallel upload streams

Returns {Object}, with properties:

  • onPause {ReactiveVar} - Is upload process on the pause?
  • progress {ReactiveVar} - Upload progress in percents
  • pause {Function} - Pause upload process
  • continue {Function} - Continue paused upload process
  • toggleUpload {Function} - Toggle continue/pause if upload in the progress
# 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}}%
collection [Isomorphic]

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')
findOne(search) [Isomorphic]
  • search {String or Object} - _id of the file or Object
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(search) [Isomorphic]
  • search {String or Object} - _id of the file or Object
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(buffer, [options], [callback]) [Server]
  • buffer {Buffer} - Binary data
  • options {Object} - Object with next properties:
    • type - File mime-type
    • size - File size
    • meta - Additional data as object, use later for search
    • name or fileName - 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'), '_parent'

meteor-files's People

Contributors

dr-dimitru avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.