Szukaj…


Uwagi

Pakiet CollectionFS został odłożony na półkę przez autora; jednak ponieważ nie ma alternatywnego pakietu w ekosystemie Atmosphere lub Meteor do korzystania z funkcji GridFS Mongo, a kod nadal działa doskonale; zalecamy, aby nie usuwać przykładu z Dokumentacji StackOverflow, dopóki niektóre inne rozwiązania GridFS nie będą mogły zostać udokumentowane jako zamienniki.

Dodatkowe badania
Filepicker.io Uploads and Image Conversion
Wzór zapisu pliku Dario
Wzór wgrywania pliku Micha Roon
Pakiet przesyłania plików EventedMind

Serwer / klient

Przesyłanie plików może być łatwe lub bardzo skomplikowane, w zależności od tego, co chcesz zrobić. Ogólnie rzecz biorąc, sam transfer pliku nie jest wcale taki trudny. Ale istnieje wiele przypadkowych przypadków dotyczących załączników, plików binarnych i tym podobnych. A prawdziwym punktem zaczepienia jest skalowanie w poziomie i tworzenie rozwiązania, które działa, gdy serwer zostanie sklonowany po raz drugi, trzeci i n-ty.

Zacznijmy od podstawowego modelu przesyłania serwera / klienta. Zaczynamy od dodania elementu wejściowego pliku do modelu obiektu dokumentu.

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

Następnie dołącz zdarzenie do elementu wejściowego w swoim kontrolerze i wywołaj lokalną metodę Meteor `` startFileTransfer '', aby zainicjować transfer.

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

Klient wywoła następnie metodę serwera saveFileToDisk, która wykonuje rzeczywisty transfer i umieszcza wszystko na dysku.

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

To rodzaj podejścia bez kości i pozostawia wiele do życzenia. Może warto przesłać plik CSV lub coś takiego, ale o to chodzi.

Dropzone (z żelazkiem: router)

Jeśli chcemy czegoś bardziej dopracowanego, ze zintegrowanym interfejsem Dropzone i punktem końcowym REST, będziemy musieli zacząć dodawać niestandardowe trasy REST i pakiety za pomocą pomocników interfejsu użytkownika.

Zacznijmy od importowania Iron Routera i Dropzone.

 meteor add iron:router
 meteor add awatson1978:dropzone

I skonfiguruj trasę do adresu URL przesyłania określoną w pomocniku dropzone.

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

Chłodny! Mamy program do przesyłania plików z niesamowitym interfejsem użytkownika i programowalnym punktem końcowym REST. Niestety nie skaluje się to szczególnie dobrze.

Filepicker.io

Aby skalować, musimy przestać używać lokalnej pamięci na naszym serwerze i zacząć korzystać z dedykowanej usługi przechowywania plików lub wdrożyć poziomą warstwę pamięci. Najłatwiejszym sposobem na rozpoczęcie korzystania ze skalowalnego magazynu plików jest skorzystanie z rozwiązania takiego jak Filepicker.io, który obsługuje S3, Azure, Rackspace i Dropbox. loadpicker od dłuższego czasu jest popularnym pakietem plików Filerpicker.

meteor add mrt:filepicker

Wzorzec Filepicker jest raczej inny niż inne rozwiązania, ponieważ tak naprawdę chodzi o integrację innych firm. Rozpocznij od dodania pliku danych do pliku, który zobaczysz, że opiera się on w dużej mierze na atrybutach data- *, co jest dość rzadkim wzorcem w aplikacjach Meteor.

<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">

Będziesz także chciał ustawić klucz API, zbudować widget do pobierania plików, uruchomić go i obserwować jego wyniki.

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

Jeśli jednak naprawdę poważnie podchodzisz do przechowywania i chcesz przechowywać miliony obrazów, musisz wykorzystać infrastrukturę GridFS Mongo i stworzyć sobie warstwę pamięci. Do tego potrzebny będzie doskonały podsystem CollectionFS.

Zacznij od dodania niezbędnych pakietów.

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

I dodanie elementu upload pliku do modelu obiektu.

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

Następnie dodaj kontroler zdarzeń na kliencie.

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

I zdefiniuj swoje kolekcje na serwerze:

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

Dzięki Razowi za ten doskonały przykład. Będziesz chciał przejrzeć pełną dokumentację CollectionFS, aby uzyskać więcej informacji na temat tego, co może zrobić wszystkie CollectionFS.

Przesyłanie z serwera

Poniższe skrypty służą do przesyłania pliku z systemu plików serwera na serwer. Głównie dla plików konfiguracyjnych i obserwatorów plików.

//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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow