Suche…


Bemerkungen

Das CollectionFS-Paket wurde vom Autor zurückgestellt und eingestellt. Da es jedoch in Atmosphere oder dem Meteor-Ökosystem kein alternatives Paket für die Verwendung der Mongo-GridFS-Funktionalität gibt, funktioniert der Code weiterhin einwandfrei. Es wird empfohlen, das Beispiel nicht aus der StackOverflow-Dokumentation zu entfernen, bis eine andere GridFS-Lösung als Ersatz dokumentiert werden kann.

Zusätzliche Forschung
Filepicker.io Uploads und Bildkonvertierung
Darios Save File Pattern
Micha Roon's File Upload Pattern
EventedMind-Paket zum Hochladen von Dateien

Server / Client

Das Hochladen von Dateien kann einfach oder sehr kompliziert sein, je nachdem, was Sie tun möchten. Im Allgemeinen ist das Übertragen einer Datei selbst nicht so schwierig. Es gibt jedoch viele Randfälle in Bezug auf Anhänge, Binärdateien und dergleichen. Der eigentliche Knackpunkt ist die horizontale Skalierung und die Schaffung einer Lösung, die funktioniert, wenn der Server ein zweites, drittes und ntes Mal geklont wird.

Beginnen wir mit einem einfachen Server / Client-Upload-Modell. Wir beginnen mit dem Hinzufügen eines Dateieingabeelements zum Dokumentobjektmodell.

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

Hängen Sie dann ein Ereignis an das Eingabeelement in Ihrem Controller an und rufen Sie die lokale Meteor-Methode `` startFileTransfer '' auf, um die Übertragung zu initiieren.

// 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);
}

Der Client ruft dann die Server-Methode saveFileToDisk auf, die die eigentliche Übertragung ausführt und alles auf die Festplatte legt.

// 
/**
 * 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,'');
    }
  }
});

Das ist eine Art nackter Ansatz, der zu wünschen übrig lässt. Es ist vielleicht gut, eine CSV-Datei hochzuladen oder so, aber das war's auch schon.

Dropzone (mit Eisen: Router)

Wenn Sie mit einer integrierten Dropzone-Benutzeroberfläche und einem REST-Endpunkt etwas mehr Feinarbeit haben möchten, müssen Sie mit dem Hinzufügen benutzerdefinierter REST-Routen und -Pakete mit Hilfe von Benutzeroberflächenhelfern beginnen.

Beginnen wir mit dem Import von Iron Router und Dropzone.

 meteor add iron:router
 meteor add awatson1978:dropzone

Und konfigurieren Sie die URL-Route für Uploads, die im Dropzone-Helper angegeben ist.

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);
            }
          });
        });
      }
    });
  });

Cool! Wir haben einen Dateiuploader mit schicker Oberfläche und einem programmierbaren REST-Endpunkt. Leider skaliert dies nicht besonders gut.

Filepicker.io

Zum Skalieren müssen wir den lokalen Speicher auf unserem Server nicht mehr verwenden und entweder einen dedizierten Dateispeicherdienst verwenden oder eine horizontale Speicherebene implementieren. Der einfachste Weg, um mit dem skalierbaren Dateispeicher zu beginnen, ist die Verwendung einer Lösung wie Filepicker.io, die S3, Azure, Rackspace und Dropbox unterstützt. loadpicker ist seit einiger Zeit ein beliebtes Filerpicker-Unipaket.

meteor add mrt:filepicker

Das Filepicker-Muster unterscheidet sich etwas von den anderen Lösungen, da es sich wirklich um die Integration von Drittanbietern handelt. Beginnen Sie mit dem Hinzufügen einer Filepicker-Eingabe, die, wie Sie sehen, stark von data- * -Attributen abhängig ist. Dies ist ein ziemlich ungewöhnliches Muster 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">

Sie möchten auch einen API-Schlüssel festlegen, das Filepicker-Widget erstellen, auslösen und dessen Ausgaben beobachten.

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

Wenn Sie es jedoch mit Speicher wirklich ernst meinen und Millionen von Bildern speichern möchten, müssen Sie die GridFS-Infrastruktur von Mongo nutzen und selbst eine Speicherebene erstellen. Dafür benötigen Sie das hervorragende CollectionFS-Subsystem.

Fügen Sie zunächst die erforderlichen Pakete hinzu.

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

Und ein Objekt zum Hochladen von Dateien zu Ihrem Objektmodell hinzufügen.

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

Fügen Sie dann einen Ereignis-Controller auf dem Client hinzu.

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
                }
            });
        });
    }
});

Und definieren Sie Ihre Sammlungen auf Ihrem 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
    }
});

Danke an Raz für dieses hervorragende Beispiel. In der vollständigen CollectionFS-Dokumentation finden Sie weitere Informationen zu allen Funktionen von CollectionFS.

Server-Uploads

Die folgenden Skripts dienen zum Hochladen einer Datei vom Server-Dateisystem auf den Server. Meistens für Konfigurationsdateien und Filewatcher.

//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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow