Zoeken…


Opmerkingen

Het CollectionFS-pakket is opgeschort en stopgezet door de auteur; omdat er echter geen alternatief pakket in Atmosphere of het Meteor-ecosysteem is voor het gebruik van Mongo's GridFS-functionaliteit, en de code werkt nog steeds prima; we raden aan het voorbeeld niet uit StackOverflow Documentation te verwijderen totdat een andere GridFS-oplossing kan worden gedocumenteerd als vervanging.

Aanvullend onderzoek
Filepicker.io Uploads en beeldconversie
Dario's Bestand opslaan
Micha Roon's bestandsuploadpatroon
EventedMind Bestand Upload Pakket

Server / Client

Het uploaden van bestanden kan eenvoudig of erg ingewikkeld zijn, afhankelijk van wat u wilt doen. Over het algemeen is het overbrengen van een bestand zelf niet zo moeilijk. Maar er zijn veel edge-cases rond bijlagen, binaire bestanden en dergelijke. En het echte knelpunt is horizontaal schalen en een oplossing creëren die werkt wanneer de server een tweede, derde en negende keer wordt gekloond.

Laten we beginnen met een basismodel voor server / client-upload. We beginnen met het toevoegen van een bestandsinvoerelement aan het documentobjectmodel.

<template name="example">
  <input type=file />
</template>

Voeg vervolgens een gebeurtenis toe aan het invoerelement in uw controller en roep een lokale Meteor-methode `` startFileTransfer '' aan om de overdracht te starten.

// client/example.js
Template.example.events({
  'change input': function(ev) {  
    _.each(ev.srcElement.files, function(file) {
      Meteor.startFileTransfer(file, file.name);
    });
  }
});

// client/save.js
/**
 * @blob (https://developer.mozilla.org/en-US/docs/DOM/Blob)
 * @name the file's name
 * @type the file's type: binary, text (https://developer.mozilla.org/en-US/docs/DOM/FileReader#Methods) 
 *
 * TODO Support other encodings: https://developer.mozilla.org/en-US/docs/DOM/FileReader#Methods
 * ArrayBuffer / DataURL (base64)
 */
Meteor.startFileTransfer = function(blob, name, path, type, callback) {
  var fileReader = new FileReader(),
    method, encoding = 'binary', type = type || 'binary';
  switch (type) {
    case 'text':
      // TODO Is this needed? If we're uploading content from file, yes, but if it's from an input/textarea I think not...
      method = 'readAsText';
      encoding = 'utf8';
      break;
    case 'binary': 
      method = 'readAsBinaryString';
      encoding = 'binary';
      break;
    default:
      method = 'readAsBinaryString';
      encoding = 'binary';
      break;
  }
  fileReader.onload = function(file) {
    Meteor.call('saveFileToDisk', file.srcElement.result, name, path, encoding, callback);
  }
  fileReader[method](blob);
}

De client roept vervolgens de servermethode saveFileToDisk aan, die de daadwerkelijke overdracht uitvoert en alles op schijf zet.

// 
/**
 * TODO support other encodings:
 * http://stackoverflow.com/questions/7329128/how-to-write-binary-data-to-a-file-using-node-js
 */
Meteor.methods({
  saveFileToDisk: function(blob, name, path, encoding) {
    var path = cleanPath(path), fs = __meteor_bootstrap__.require('fs'),
      name = cleanName(name || 'file'), encoding = encoding || 'binary',
      chroot = Meteor.chroot || 'public';
    // Clean up the path. Remove any initial and final '/' -we prefix them-,
    // any sort of attempt to go to the parent directory '..' and any empty directories in
    // between '/////' - which may happen after removing '..'
    path = chroot + (path ? '/' + path + '/' : '/');

    // TODO Add file existance checks, etc...
    fs.writeFile(path + name, blob, encoding, function(err) {
      if (err) {
        throw (new Meteor.Error(500, 'Failed to save file.', err));
      } else {
        console.log('The file ' + name + ' (' + encoding + ') was saved to ' + path);
      }
    }); 

    function cleanPath(str) {
      if (str) {
        return str.replace(/\.\./g,'').replace(/\/+/g,'').
          replace(/^\/+/,'').replace(/\/+$/,'');
      }
    }
    function cleanName(str) {
      return str.replace(/\.\./g,'').replace(/\//g,'');
    }
  }
});

Dat is een soort van kale aanpak en laat veel te wensen over. Het is misschien goed voor het uploaden van een CSV-bestand of zoiets, maar dat is het dan ook.

Dropzone (met ijzer: router)

Als we iets meer gepolijst willen, met een geïntegreerde Dropzone UI en een REST-eindpunt, moeten we beginnen met het toevoegen van aangepaste REST-routes en pakketten met UI-helpers.

Laten we beginnen met het importeren van Iron Router en Dropzone.

 meteor add iron:router
 meteor add awatson1978:dropzone

En configureer de upload-URL-route die is opgegeven in de dropzone-helper.

Router.map(function () {
    this.route('uploads', {
      where: 'server',
      action: function () {
        var fs = Npm.require('fs');
        var path = Npm.require('path');
        var self = this;

        ROOT_APP_PATH = fs.realpathSync('.');

        // dropzone.js stores the uploaded file in the /tmp directory, which we access
        fs.readFile(self.request.files.file.path, function (err, data) {

          // and then write the file to the uploads directory
          fs.writeFile(ROOT_APP_PATH + "/assets/app/uploads/" +self.request.files.file.name, data, 'binary', function (error, result) {
            if(error){
              console.error(error);
            }
            if(result){
              console.log('Success! ', result);
            }
          });
        });
      }
    });
  });

Koel! We hebben een bestandsuploader met een hippe gebruikersinterface en een programmeerbaar REST-eindpunt. Helaas schaalt dit niet bijzonder goed.

Filepicker.io

Om dingen te schalen, moeten we stoppen met het gebruik van lokale opslag op onze server en beginnen met het gebruik van een speciale bestandsopslagservice of een horizontale opslaglaag implementeren. De eenvoudigste manier om aan de slag te gaan met schaalbare bestandsopslag is een oplossing te gebruiken zoals Filepicker.io, die S3, Azure, Rackspace en Dropbox ondersteunt. loadpicker is al een tijdje een populaire Filerpicker-unipackage.

meteor add mrt:filepicker

Het Filepicker-patroon is nogal anders dan de andere oplossingen, omdat het echt om integratie met derden gaat. Begin met het toevoegen van een filepicker-ingang, waarvan je zult zien dat deze sterk afhankelijk is van data- * attributen, wat een vrij ongewoon patroon is in Meteor-apps.

<input type="filepicker"
  id="filepickerAttachment"
  data-fp-button-class="btn filepickerAttachment"
  data-fp-button-text="Add image" 
  data-fp-mimetypes="image/*"
  data-fp-container="modal"
  data-fp-maxsize="5000000" 
  data-fp-services="COMPUTER,IMAGE_SEARCH,URL,DROPBOX,GITHUB,GOOGLE_DRIVE,GMAIL">

U wilt ook een API-sleutel instellen, de filepicker-widget construeren, activeren en de uitgangen ervan observeren.

if(Meteor.isClient){
  Meteor.startup(function() {
    filepicker.setKey("YourFilepickerApiKey");
  });
  Template.yourTemplate.rendered = function(){
    filepicker.constructWidget($("#filepickerAttachment"));
  }
  Template.yourTemplate.events({
  'change #filepickerAttachment': function (evt) {
    console.log("Event: ", evt, evt.fpfile, "Generated image url:", evt.fpfile.url);
  });
});

CollectionFS

Als je echter echt serieus bent over opslag en je wilt miljoenen afbeeldingen opslaan, moet je de GridFS-infrastructuur van Mongo benutten en zelf een opslaglaag maken. Daarvoor heb je het uitstekende CollectionFS-subsysteem nodig.

Begin met het toevoegen van de nodige pakketten.

meteor add cfs:standard-packages
meteor add cfs:filesystem

En het toevoegen van een bestandsuploadelement aan uw objectmodel.

<template name="yourTemplate">
    <input class="your-upload-class" type="file">
</template>

Voeg vervolgens een gebeurteniscontroller toe aan de client.

Template.yourTemplate.events({
    'change .your-upload-class': function(event, template) {
        FS.Utility.eachFile(event, function(file) {
            var yourFile = new FS.File(file);
            yourFile.creatorId = Meteor.userId(); // add custom data
            YourFileCollection.insert(yourFile, function (err, fileObj) {
                if (!err) {
                   // do callback stuff
                }
            });
        });
    }
});

En definieer uw collecties op uw server:

YourFileCollection = new FS.Collection("yourFileCollection", {
    stores: [new FS.Store.FileSystem("yourFileCollection", {path: "~/meteor_uploads"})]
});
YourFileCollection.allow({
    insert: function (userId, doc) {
        return !!userId;
    },
    update: function (userId, doc) {
        return doc.creatorId == userId
    },
    download: function (userId, doc) {
        return doc.creatorId == userId
    }
});

Met dank aan Raz voor dit uitstekende voorbeeld. U wilt de volledige CollectionFS-documentatie bekijken voor meer informatie over wat alle CollectionFS kan doen.

Server-uploads

De volgende scripts zijn voor het uploaden van een bestand van het serverbestandssysteem naar de server. Meestal voor configuratiebestanden en filewatchers.

//https://forums.meteor.com/t/read-file-from-the-public-folder/4910/5

// Asynchronous Method.
Meteor.startup(function () {
    console.log('starting up');

    var fs = Npm.require('fs');
    // file originally saved as public/data/taxa.csv
    fs.readFile(process.cwd() + '/../web.browser/app/data/taxa.csv', 'utf8', function (err, data) {
        if (err) {
            console.log('Error: ' + err);
            return;
        }

        data = JSON.parse(data);
        console.log(data);
    });
});


// Synchronous Method.
Meteor.startup(function () {
    var fs = Npm.require('fs');
    // file originally saved as public/data/taxa.csv
    var data = fs.readFileSync(process.cwd() + '/../web.browser/app/data/taxa.csv', 'utf8');

    if (Icd10.find().count() === 0) {
        Icd10.insert({
            date:  new Date(),
            data:  JSON.parse(data)
        });
    }
});


Meteor.methods({
  parseCsvFile:function (){
    console.log('parseCsvFile');

    var fs = Npm.require('fs');
    // file originally saved as public/data/taxa.csv
    var data = fs.readFileSync(process.cwd() + '/../web.browser/app/data/taxa.csv', 'utf8');
    console.log('data', data);
  }
});


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow