20 Stimmen

Generieren und Bereitstellen von statischen Dateien mit Meteor

Ich möchte statische Textdateien erstellen, die auf dem Inhalt eines gelieferten Objekts basieren und die dann vom Benutzer heruntergeladen werden können. Hier ist, was ich geplant hatte zu tun:

  1. Wenn der Benutzer auf 'exportieren' klickt, ruft die Anwendung eine Meteor.method() auf, die wiederum die Datei im öffentlichen Verzeichnis unter Verwendung üblicher Node-Methoden analysiert und schreibt.

  2. Sobald die Datei erstellt ist, biete ich in der Rückruf-Funktion von Meteor.method() einen Link zur generierten Datei an. Zum Beispiel 'public/userId/file.txt'. Der Benutzer kann dann wählen, die Datei über diesen Link herunterzuladen.

  3. Dann verwende ich Meteors Connect-Modul (das es intern verwendet), um Anfragen an die obige URL zur Datei selbst weiterzuleiten. Ich könnte einige Berechtigungsprüfungen basierend auf der Benutzer-ID und dem eingeloggten Zustand des Benutzers durchführen.

Das Problem: Wenn statische Dateien im öffentlichen Verzeichnis generiert werden, lädt sich die Webseite automatisch neu. Ich dachte, es könnte sinnvoller sein, etwas wie Express zu verwenden, um einen REST-Endpunkt zu generieren, der sich mit der Erstellung der Dateien befasst. Aber dann bin ich mir nicht sicher, wie ich mit Berechtigungen umgehen soll, wenn ich keinen Zugriff auf die Meteor-Sitzungsdaten habe.

Ideen für die beste Strategie hier?

25voto

Matyas Punkte 12853

In der Version 0.6.6.3 0.7.x - 1.3.x können Sie Folgendes tun:

Zum Schreiben

var fs = Npm.require('fs');
var filePath = process.env.PWD + '/.uploads_dir_on_server/' + fileName;
fs.writeFileSync(filePath, data, 'binary');

Zum Servieren

In einem normalen Meteor-App

var fs = Npm.require('fs');
WebApp.connectHandlers.use(function(req, res, next) {
    var re = /^\/uploads_url_prefix\/(.*)$/.exec(req.url);
    if (re !== null) {   // Nur URLs, die mit /uploads_url_prefix/* beginnen, behandeln
        var filePath = process.env.PWD + '/.uploads_dir_on_server/' + re[1];
        var data = fs.readFileSync(filePath);
        res.writeHead(200, {
                'Content-Type': 'image'
            });
        res.write(data);
        res.end();
    } else {  // Andere URLs haben Standardverhalten
        next();
    }
});

Bei Verwendung von iron:router

Dies sollte eine serverseitige Route sein (z. B. in einer Datei im /server/ Ordner definiert)

Bearbeitung (2016. Mai-9)

var fs = Npm.require('fs');
Router.route('uploads', {
       name: 'uploads',
       path: /^\/uploads_url_prefix\/(.*)$/,
       where: 'server',
       action: function() {
           var filePath = process.env.PWD + '/.uploads_dir_on_server/' + this.params[0];
           var data = fs.readFileSync(filePath);
           this.response.writeHead(200, {
               'Content-Type': 'image'
           });
           this.response.write(data);
           this.response.end();
       }
    });

Veraltete Format:

Router.map(function() {
    this.route('serverFile', {
        ...// genau wie das obige Objekt
    }
});

Anmerkungen

  • process.env.PWD gibt Ihnen das Projektverzeichnis

  • Wenn Sie Dateien innerhalb Ihres Projekts platzieren möchten

    • verwenden Sie nicht die public oder private Meteor-Ordner
    • verwenden Sie Punktfolder (z. B. versteckte Ordner z. B. .uploads)

    Wenn Sie diese beiden Regeln nicht beachten, wird der lokale Meteor bei jedem Upload neu gestartet, es sei denn, Sie führen Ihre Meteor-App mit aus: meteor run --production

  • Ich habe diesen Ansatz für einen einfachen Bildupload und -dienst verwendet (basierend auf dario's Version)

  • Wenn Sie eine komplexere Dateiverwaltung wünschen, sollten Sie CollectionFS in Betracht ziehen

16voto

Jacott Punkte 675

Der Symlink-Hack funktioniert nicht mehr in Meteor (ab 0.6.5). Stattdessen schlage ich vor, ein Paket mit ähnlichem Code wie folgt zu erstellen:

packge.js

Package.describe({
  Zusammenfassung: "Anwendungsdateiserver."
});

Npm.depends({
  connect: "2.7.10"
});

Package.on_use(function(api) {
  api.use(['webapp', 'routepolicy'], 'server');

  api.add_files([
    'app-file-server.js',
  ], 'server'); 
});

app-file-server.js

var connect = Npm.require('connect');

RoutePolicy.declare('/my-uploaded-content', 'network');

// Anfragen annehmen
WebApp.connectHandlers
  .use('/my-uploaded-content', connect.static(process.env['APP_DYN_CONTENT_DIR']));

5voto

dustin.b Punkte 1255

Ich steckte genau vor dem gleichen Problem fest, bei dem ich die Benutzer dazu bringen muss, Dateien hochzuladen, im Gegensatz zu Ihren servergenerierten Dateien. Ich habe es so gelöst, dass ich einen "uploads" -Ordner als Geschwister des "Client-Public-Servers" auf der gleichen Ordnerebene erstellt habe. Und dann habe ich einen symbolischen Link zum Ordner '.meteor/local/build/static' erstellt wie

ln -s ../../../../uploads .meteor/local/build/static/

aber mit dem Node.js-Dateisystem-API zur Serverstartzeit

Meteor.startup(function () {
    var fs = Npm.require('fs');

    fs.symlinkSync('../../../../uploads', '.meteor/local/build/static/uploads'); 
};

In Ihrem Fall haben Sie möglicherweise einen Ordner wie "generatedFiles" anstelle meines "uploads" -Ordners. Sie müssen dies jedes Mal tun, wenn der Server gestartet wird, da diese Ordner jedes Mal generiert werden, wenn der Server gestartet wird, z. B. wenn eine Datei in Ihrer Implementierung geändert wird.

1voto

Sean Punkte 4045

Eine weitere Option ist die Verwendung einer serverseitigen Route, um den Inhalt zu generieren und an den Browser des Benutzers zum Download zu senden. Zum Beispiel wird mit dem folgenden Code ein Benutzer anhand seiner ID gesucht und als JSON zurückgegeben. Der Endbenutzer wird aufgefordert, die Antwort in einer Datei mit dem im Content-Disposition-Header angegebenen Namen zu speichern. Auch andere Header wie Expires können der Antwort hinzugefügt werden. Wenn der Benutzer nicht existiert, wird ein 404 zurückgegeben.

Router.route("userJson", {
    where: "server",

    path: "/user-json/:userId",

    action: function() {
        var user = Meteor.users.findOne({ _id: this.params.userId });

        if (!user) {
            this.response.writeHead(404);
            this.response.end("Benutzer nicht gefunden");
            return;
        }

        this.response.writeHead(200, {
            "Content-Type": "application/json",
            "Content-Disposition": "attachment; filename=user-" + user._id + ".json"
        });
        this.response.end(JSON.stringify(user));
    }
});

Diese Methode hat jedoch einen großen Nachteil. Serverseitige Routen bieten keine einfache Möglichkeit, den aktuell angemeldeten Benutzer zu erhalten. Siehe dieses Problem auf GitHub.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X