So einfach exportierst du mit Strapi deine Daten als CSV

So einfach exportierst du mit Strapi deine Daten als CSV

Ich nutze nun schon eine ganze Weile Strapi. Einige wichtige Features fehlen allerdings. Ich habe im letzten Artikel bereits beschrieben, wie sich mit Strapi einfach Thumbnails generieren lassen. Heute geht es um die Frgage, wie man in Strapi eine einfache Exportfunktion implementieren kann. Los gehts!

Dieses Tutorial funktioniert für Strapi 3.x.x

Die Grundidee

Strapi hat leider keine eigene Export-Funktion für verschiedene Datensätze. Und auch wenn es diese gäbe: Für diveres Kundenansprüche wird manchmal nur eine ganz individuelle Lösung helfen, die auch in der Lage ist, verschiedene Daten zu aggregieren oder zu filtern.

Wenn wir Strapi beibringen wollen Daten zu exportieren, müssen wir uns als Erstes fragen, was Strapi am besten kann: Richtig. Daten verwalten!

Die einfachste Lösung besteht also darin, einen neuen Datentyp mit dem Namen "Export" zu generieren. Dadurch lassen sich die exportierten Datensätze dann schonmal innerhalb von Strapi verwalten. Durch einen Hook, beim Erstellen eines neuen Exports generieren wir einfach die angeforderte Export-Datei. Wir müssen nur noch dafür sorgen, dass diese dann auch an den neuen Export-Datensatz angehangen wird. Klingt einfach? Ist es auch!

Erstellen des Export-Datentyps

Erstelle zunächst einen neuen Datentyp mit dem Namen "Export" und erstelle die folgenden Felder:

  • file(File)
  • source(ENUM)
  • from(Date)
  • to(Date)
  • createdAt(Text)
  • updatedAt(Text)

Trage für das Feld "source" als mögliche Werte die zu exportierenden Datensätze aus deinem System ein. Als Beispiel verwende ich hier "product" und "customer":
Also: product,customer

Der Code im Detail

Schau dir die folgende Datei an. Es handelt sich um die Datei "api/export/models/Export.js" Diese wurde beim Anlegen des Datentyps "Export" durch das Backend erzeugt und von mir entsprechend angepasst:

'use strict';

/**
 * Lifecycle callbacks for the `Export` model.
 */

const path = require('path');
const fs = require('fs');
var mongoose = require('mongoose');

const exportsPath = path.join(__dirname, '../../../public/GCG7XSfbML6JUo1YCszn_exports');

async function generateCsv(type, id, rows){

  // Generate CSV
  var string = '';
  for(var row of rows){
    string = string + '"' +row.join('";"')+'"'+"\n";
  }

  // Write CSV
  fs.writeFile(exportsPath+'/'+type+'_'+id+'.csv', string, 'utf8', function(err) {
    if(err) {
      console.log(err);
    }
    // console.log('The export was saved');
  });

  // Create file in database
  const fileDocument = await strapi.plugins.upload.models.file.create({
    name: id+'.csv',
    ext: '.csv',
    url: '/GCG7XSfbML6JUo1YCszn_exports/'+type+'_'+id+'.csv',
    provider: 'local',
    hash: id, // This is just the filename without the extension
    size: 0, // I think this will be ok
    mime: 'text/csv',
    related: {
      _id: mongoose.Types.ObjectId(),
      ref: id,
      kind: 'Export',
      field: 'file'
    }
  });
}

function prepareData(string){

  if(string!=undefined){

    string = String(string);

    // Escape for CSV
    string = string.replace(/;/g,''); // replace ;
    string = string.replace(/"/g,"'"); // replace "
    string = string.replace(/(?:\r\n|\r|\n)/g,' '); // replace newlines

    return string;

  }
  return '';
}

module.exports = {

  // After creating a value.
  // Fired after an `insert` query.
  afterCreate: async (model, result) => {  

    var rows = [];

    // Do we have a start date?
    var where = {};

    if(model.from != undefined){
      if(model.from != null){
        if(where.createdAt == undefined){
          where.createdAt = {}
        }
        where.createdAt.$gte = new Date(model.from)
      }
    }

    // Is there an end date?
    if(model.to != undefined){
      if(model.to != null){
        if(where.createdAt == undefined){
          where.createdAt = {}
        }
        where.createdAt.$lte = new Date(model.to)
      }
    }

    // Export the products
    if(model.source=='product'){

      rows.push(['_id', 'name', 'description', 'price', 'stock']);

      var documents = await strapi.models.report.find(where);

      if(documents){

        // For each document
        for(var document of documents) {
          rows.push([
            prepareData(document['_id']),
            prepareData(document['name']),
            prepareData(document['description']),
            prepareData(document['price']),
            prepareData(document['stock'])
          ]);
        }

      }

      generateCsv('product', model._id, rows);

    }

    // Export the customers
    if(model.source=='customer'){

      rows.push(['_id', 'name', 'email', 'street', 'city']);

      var documents = await strapi.models.motif.find(where);

      if(documents){

        // For each document
        for(var document of documents) {
          rows.push([
            prepareData(document['_id']),
            prepareData(document['name']),
            prepareData(document['email']),
            prepareData(document['street']),
            prepareData(document['city'])
          ]);
        }

      }

      generateCsv('customer', model._id, rows);

    }


  }
  
};

Zunächst definieren wir einen Speicherort für die Exports. In diesem Fall ist das "public/GCG7XSfbML6JUo1YCszn_exports".

Warnung: Bitte wähle dir einen eigenen kryptischen Namen für die Speicherung der Export-Datei. Die Dateien werden später in einem öffentlich zugänglichen Bereich der Website liegen und müssen daher gegen Brute-Force geschützt werden. Außerdem solltest du deinen Webserver so konfigurieren, dass das betroffene Verzeichnis mit einem Basic-Auth geschützt ist!

Danach wird die Methode zur Erzeugung der CSV-Datei "generateCsv()" definiert. Diese generiert zunächst die Datei und verknüpft danach das File in der Datenbank mit unserem neu erstellten Export-Datensatz.

Die Methode "prepare" hilft dabei, die einzelnen Inhalte für die CSV-Datei vorzubereiten.

Wie du siehst nutzen wir hier den "afterCreate" Hook, um zu prüfen, welcher Datensatz exportiert werden soll. Dazu dient das Feld "source". Aus diesem können nun die zu exportierenden Daten ausgewählt werden.

Die Felder "from" und "to" dienen dazu, ein Where-Statement zu erstellen, welches es uns erlaubt die Daten grob zeitlich zu filtern. Das ist natürlich noch ausbaufähig. Du kannst jedes nur erdenkliche Feld für die Generierung im Backend erstellen und den Filter so noch weiter ausbauen.

Wie du siehst habe ich für jeden Datentyp eine eigene kleine Export-Routine erstellt. Diese werden nur ausgeführt, wenn der richtige Datentyp aus dem Feld "source" gewählt wurde: "if(model.source=='customer')...". Innerhalb jeder Routine wird definiert wie die Zeilen des jeweiligen Exports aufgebaut werden, und welche Daten diese enthalten sollen.

Generiere deinen ersten CSV Export

Erstelle im Backend einen neuen Datensatz des Typs "Export". Wähle dann die Quelle, die du exportieren möchtest aus und speichere den neuen Datensatz. Wenn du möchtest kannst du auch die Datumsfelder ausfüllen und den Export so einschränken.

Du kannst den Export nun direkt herunterladen. Öffne dazu den neu generierten Datensatz im Backend. Das Datei-Feld sollte nun mit der generierten Datei verbunden sein. Über den kleinen Link unter dem Dateifeld kannst du dir die Datei nun herunterladen.

Ich hoffe ich konnte dir die Basics dieser simplen Export-Idee vermitteln. Das alles ist natürlich nur eine Demo. Passe dir das Script so an, wie du es für dein Projekt brauchst.

Titelbild: https://pixabay.com/de/photos/container-hafen-verladen-gestapelt-3118782/