[PYTHON] Spielen Sie, indem Sie den japanischen Dialektkorpus in eine Datenbank konvertieren. (8) Fügen Sie eine Konvertierungsfunktion für das Dateiformat hinzu

Dies ist ein serialisierter Artikel. Da wir bis zum letzten Mal eine Liste von Diskursen und Äußerungen für jeden Sprecher erstellt haben, implementieren wir dieses Mal die endgültige Funktion "Gegenseitige Konvertierung zwischen Excel und TextGrid auf der Site". Es ist ein Arbeitsmemo für mich und ich glaube nicht, dass es genug Erklärungen gibt, aber bitte vergib mir.

Wie bereits erwähnt, haben wir dieses Mal bereits ein Python-Skript, das TextGrid und Excel konvertiert (wir werden nicht auf Details eingehen). Daher möchten wir es in eine Laravel-App einbetten und auf dem Server ausführen.

--Teil 1: Spielen durch Konvertieren des japanischen Dialektkorpus in eine Datenbank (1) Nachdenken über die Konfiguration --Teil 2: Mit japanischem Dialektkorpus als DB spielen (2) Mit SQLite3 in DB konvertieren

Vorbereitungen

Dieses Mal speichern wir die Datei im Speicher auf dem Server, konvertieren sie und erstellen einen Mechanismus zum Herunterladen. Nehmen Sie also zuerst die entsprechenden Einstellungen vor. Hochgeladene Dateien werden im Ordner "storage" gespeichert. Da es sich jedoch um den Ordner "public" handelt, der für die Öffentlichkeit zugänglich ist, ist es üblich, einen symbolischen Link von "public / storage" zu "storage / app / public" zu erstellen. Sie können es selbst mit dem Handwerkerbefehl unter [^ symbolisch] einfügen.

[^ symbolisch]: Wie später beschrieben wird, werden lokal erstellte symbolische Links nicht ohne Erlaubnis auf Heroku erstellt. Schreiben Sie daher die erforderlichen Anweisungen in composer.json und erstellen Sie beim Erstellen einen symbolischen Link. Sie müssen in der Lage sein.

cmd


php artisan storage:link

Bildschirmübergangsdiagramm

Das Bildschirmübergangsdiagramm wird erneut veröffentlicht. Es ist eine Seite.

func_3.png

Komponenten-Routing

Da es nur eine Komponente gibt, gibt es nichts Besonderes zu erklären.

resources/js/app.js


+ import ConvertComponent from "./components/ConvertComponent";

+        {
+            path: "/convert",
+            name: "convert",
+            component: ConvertComponent
+        }

Erstellen einer Komponente

Obwohl es sich um einen Bildschirm handelt, ist er komplizierter als beim letzten Mal, da er viele Funktionen hat.

resouces/js/components/ConvertComponent.vue


<template>
  <div>
    <form enctype="multipart/form-data">
      <input
        type="file"
        name="file"
        id="fileRef"
        style="display: none"
        @change="fileSelected"
      />
      <div class="input-group">
        <input
          type="text"
          id="fileShow"
          class="form-control"
          placeholder="select file..."
          readonly
        />
        <div class="input-group-append">
          <span class="input-group-btn">
            <button 
              type="button" 
              class="btn btn-outline-success" 
              onclick="fileRef.click()"
            >
              Browse
            </button>
          </span>
          <button 
            type="button" 
            class="btn btn-success" 
            @click="fileUpload"
          >
            Upload
          </button>
        </div>
      </div>
    </form>
    <div class="pt-3">
      <table class="table table-sm table-striped">
        <thead>
          <tr class="thead-dark">
            <th colspan="2">
              <div class="text-center">Dateiliste</div>
            </th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="file of files" v-bind:key="file.name">
            <td>
              <span class="pl-3">{{ file.replace("public/", "") }}</span>
            </td>
            <td>
              <div class="text-right">
                <span 
                  class="btn btn-success btn-sm" 
                  @click="toTextgrid(file)" 
                  v-if="file.indexOf('.xls') != -1"
                >
                  to TextGrid
                </span>
                <span 
                  class="btn btn-outline-success btn-sm disabled" 
                  v-else
                >
                  to TextGrid
                </span>
                <span 
                  class="btn btn-success btn-sm" 
                  @click="toExcel(file)" 
                  v-if="file.indexOf('.txt') != -1 || file.indexOf('.TextGrid') != -1"
                >
                  to Excel
                </span>
                <span 
                  class="btn btn-outline-success btn-sm disabled" 
                  v-else
                >
                  to Excel
                </span>
                <a 
                  v-bind:href="'./storage' + file.replace('public', '')" 
                  v-bind:download="file.replace('public', '')"
                >
                  <span class="btn btn-warning btn-sm">
                    download
                  </span>
                </a>
                <span 
                  class="btn btn-danger btn-sm" 
                  @click="deleteFile(file)"
                >
                  delete
                </span>
              </div>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>

<script>
export default {
  data: function() {
    return {
      files: [],
      uploadingFileInfo: ""
    };
  },
  methods: {
    fileSelected(event) {
      this.uploadingFileInfo = event.target.files[0];
      fileShow.value = fileRef.value.replace("C:\\fakepath\\", "");
    },
    fileUpload() {
      if (this.uploadingFileInfo) {
        const formData = new FormData();
        formData.append("file", this.uploadingFileInfo);
        axios.post("/api/toolkit/upload", formData).then(res => {
          fileRef.value = "";
          fileShow.value = "";
          this.uploadingFileInfo = "";
          this.getFileList();
        });
      } else {
        alert("Bitte wählen Sie die hochzuladende Datei aus");
      }
    },
    getFileList() {
      axios.get("/api/convert/files").then(res => {
        this.files = res.data;
      });
    },
    to_textgrid(path) {
      axios.post("/api/convert/toTextgrid", { filepath: path }).then(() => {
        this.getFileList();
      });
    },
    to_excel(path) {
      axios.post("/api/convert/toExcel", { filepath: path }).then(() => {
        this.getFileList();
      });
    },
    deleteFile(path) {
      axios.post("/api/convert/delete", { filepath: path }).then(() => {
        this.getFileList();
      });
    }
  },
  mounted() {
    this.getFileList();
  }
};
</script>

Dateiauswahlformular

Das Dateiformat sieht mit Bootstrap allein nicht sehr gut aus. Einige einfache Methoden wurden entwickelt, aber dieses Mal habe ich auf die folgende Seite verwiesen.

Schaltfläche "Konvertieren"

Die Schaltflächen [zu TextGrid] und [zu Excel] werden entsprechend der Dateierweiterung umgeschaltet, sodass sie nur durch Klicken ausgelöst werden, wenn die Erweiterung geeignet ist. Da es ein Format namens TextGrid gibt, ist es nicht möglich, die Fallklassifizierung nach "Mimetyp" zu verwenden. Daher wird einfach klassifiziert, ob der Dateiname eine Zeichenfolge wie ".txt" oder ".TextGrid" enthält [^ validate]. ]. Die Fallklassifikation selbst ist "v-if", "v-else".

[^ validate]: Eigentlich handelt es sich nicht um eine Überprüfung eines solchen Frontends, aber die Eingabedatei muss auf der Serverseite ordnungsgemäß überprüft werden.

<!-- .txt/.TextGrid zeigt die obige Aktivierungsschaltfläche an-->
<span
    class="btn btn-success btn-sm"
    @click="toExcel(file)"
    v-if="file.indexOf('.txt') != -1 || file.indexOf('.TextGrid') != -1"
>
    to Excel
</span>
<!--Wenn nicht, zeigen Sie die Deaktivierungsschaltfläche unten-->
<span
    class="btn btn-outline-success btn-sm disabled"
    v-else
>
    to Excel
</span>

Download-Button

Verschiedene Konvertierungen und Löschungen werden durch Klicken ausgeführt, um die Funktion auszuführen, aber nur der Download hat einen direkten Link zur Datei. Es gibt verschiedene Möglichkeiten, Dateien vom Laravel-Server herunterzuladen, aber die Methode mit der Fassade "Speicher" und "response ()" hat nicht funktioniert (mehrere Verluste) [^ down]. Verknüpfen Sie also direkt. Ich habe die Methode des Einfügens übernommen.

Wenn Sie den Dateipfad mit einer einfachen Methode abrufen, wie später beschrieben, wird der Pfad unter "/ storage / app" zurückgegeben (der Pfad wie = "/ public / filename.ext" wird zurückgegeben). Ersetzen Sie ihn daher entsprechend und symbolisch. Fügen Sie den Link direkt in die vorherige Datei "(/ public) / storage / filename.ext" ein.

Download-Link


<a 
  v-bind:href="'./storage' + file.replace('public', '')" 
  v-bind:download="file.replace('public', '')"
>
  <span class="btn btn-warning btn-sm">
    download
  </span>
</a>

[^ down]: Die Pfadauflösung ist fehlgeschlagen, es ist ein 403-Fehler aufgetreten, und der Dateiinhalt wurde in der POST-Antwort gestapelt, konnte jedoch nicht heruntergeladen werden.

Routing zum Controller

Da alles in "FileController" implementiert ist, schreiben Sie das Routing in "api.php", wobei Sie den Funktionsnamen entsprechend berücksichtigen.

routes/api.php


+ Route::get('/convert/files', 'FileController@getFileList');
+ Route::post('/convert/upload', 'FileController@upload');
+ Route::post('/convert/e_t', 'FileController@toTextgrid');
+ Route::post('/convert/t_e', 'FileController@toExcel');
+ Route::post('/convert/delete', 'FileController@deleteFile');

Controller erstellen

Wir werden die fünf Funktionen implementieren, die wir zuvor verwendet haben.

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;

class FileController extends Controller{

    //Laden Sie die Datei hoch und speichern Sie sie
    public function upload(Request $request){
        $filename = $request->file('file')->getClientOriginalName();
        $request->file('file')->storeAs('public/',$filename);
    }
    
    //Konvertieren Sie Excel in TextGrid und speichern Sie es
    public function toTextgrid(Request $request) {
        exec("which python", $pythonpath);

        $scriptpath = app_path('Python/excel_to_textgrid.py');
        $filepath = storage_path('app/' . $request->input('filepath'));

        $command = $pythonpath[0] . ' ' . $scriptpath . ' ' . $filepath;
        exec($command);
    }
    
    //Konvertieren Sie TextGrid in Excel und speichern Sie es, fast wie oben
    public function toExcel(Request $request) {
        exec("which python", $pythonpath);

        $scriptpath = app_path('Python/textgrid_to_excel.py');
        $filepath = storage_path('app/' . $request->input('filepath'));

        $command = $pythonpath[0] . ' ' . $scriptpath . ' ' . $filepath;
        exec($command);
    }

    //Holen Sie sich eine Liste der Dateien
    public function getFileList(){
        //wahr ist.Punktdateien wie gitignore ausschließen
        $files = Storage::allfiles('public/', true);
        //Der SplFileInfo-Typ ist in Javascript umständlich, daher wird er als Dateipfadzeichenfolge zurückgegeben (fehlerhaft?)
        $filepaths = explode('#', implode('#', $files));
        return $filepaths;
    }

    //Datei löschen
    public function deleteFile(Request $request){
        $filepath = $request->input('filepath');
        Storage::delete($filepath);
    }
}

Führen Sie das Python-Skript aus

Solange Python auf Ihrem Server installiert ist, können Sie Python mit dem PHP-Befehl exec ausführen. Wie wir später sehen werden, vergessen Sie nicht, die Module zu installieren, die Sie beim Erstellen von Heroku mit Python verwenden.

Da Heroku ein Linux-System ist, [^ dyno], werde ich es unter Berücksichtigung des Linux-Befehls schreiben. Dieses Mal habe ich es unter Windows 10 ohne Containervirtualisierung entwickelt, aber da dieser Artikel ein wenig behandelt, gab es kein großes Problem.

[^ dyno]: Heroku verwendet Dyno, einen leichten Linux-Container, der auf einer riesigen Instanz von Amazon EC2 ausgeführt wird.

Das auszuführende Verfahren ist einfach. Das diesmal verwendete Skript lautet "Geben Sie den Pfad der Zieldatei an, konvertieren Sie diese Datei und speichern Sie sie im selben Verzeichnis", also ** Pfad der Python-Ausführungsdatei, Skriptpfad, Pfad der Zieldatei ** Alles was Sie tun müssen, ist es zu bekommen und einen darauf basierenden Befehl zu erstellen. Dieses Mal wird das Skript unter "/ app / Python" abgelegt, sodass Sie den Pfad mithilfe eines Pfadhilfsprogramms wie "app_path" sicher abrufen können (andernfalls ist es gegen Routenfehlausrichtungen instabil). ).

<?php
//Konvertieren Sie Excel in TextGrid und speichern Sie es
public function toTextgrid(Request $request) {
    //Rufen Sie den Pfad zu Python in der Ausführungsumgebung ab
    //Exec für Windows cmd("where python", $pythonpath);
    exec("which python", $pythonpath);

    //Rufen Sie den Pfad des Python-Skripts ab, das Sie ausführen möchten
    $scriptpath = app_path('Python/excel_to_textgrid.py');

    //Holen Sie sich den POSTed-Dateipfad und konvertieren Sie ihn in den entsprechenden relativen Pfad
    $filepath = storage_path('app/' . $request->input('filepath'));

    //Befehle zusammenstellen und ausführen
    //Beachten Sie den Index, wenn Ihre Umgebung mehrere Versionen von Python enthält
    $command = $pythonpath[0] . ' ' . $scriptpath . ' ' . $filepath;
    exec($command);
}

Das hier verwendete Skript speichert die Ausgabedatei im selben Verzeichnis wie die Eingabedatei.

Abschlusszeichnung

Es sollte so aussehen.

/convert convert.png

Verbesserungspunkte

Zusätzlich zu der oben erwähnten Fehlerbehandlung ist "exec" ein großes Sicherheitsproblem. Im Allgemeinen ist es sehr gefährlich, nur Daten, die vom Benutzer manipuliert werden können, in die "exec" -Funktion von PHP zu werfen, daher müssen sie entsprechend maskiert werden. Dieses Mal gehe ich durch Laravels Pfadhelfer, also denke ich, dass es in Ordnung ist, aber wenn Sie das genaue Verhalten des Helfers nicht kennen, sollten Sie alles in Ihrer Macht stehende tun.

nächstes Mal

Ich werde es zu Heroku (endgültig) erheben.

Recommended Posts

Spielen Sie, indem Sie den japanischen Dialektkorpus in eine Datenbank konvertieren. (8) Fügen Sie eine Konvertierungsfunktion für das Dateiformat hinzu
Spielen Sie, indem Sie den japanischen Dialektkorpus in eine Datenbank umwandeln. (4) Legen Sie das Gesamtbild des Dienstes fest
Legen Sie die von django hochgeladene Datei in MinIO ab
[Python] Ein Notizbuch, das die ipynb-Datei von GitHub ins Japanische übersetzt und herunterlädt.
Fügen Sie eine Funktion hinzu, um dem Wetter heute mitzuteilen, dass der Bot locker ist (hergestellt von Python).