Bild-Upload und -Manipulation mit React

Avatar of Damon Bauer
Damon Bauer am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Der folgende Beitrag ist ein Gastbeitrag von Damon Bauer, der sich mit einer ziemlich üblichen Aufgabe für Webentwickler befasst: dem Anbieten von Benutzerbild-Uploads. Ich würde zögern, es als einfach zu bezeichnen, aber mit Hilfe einiger leistungsstarker Werkzeuge, die viel Arbeit erledigen, ist diese Aufgabe um ein Vielfaches einfacher geworden als früher. Damon macht es sogar komplett im Browser!

Eine häufige Aufgabe für Webentwickler ist es, Benutzern die Möglichkeit zum Hochladen von Bildern zu geben. Zunächst mag es trivial erscheinen, aber es gibt Dinge zu bedenken, wenn man eine Bild-Upload-Komponente erstellt. Hier sind nur einige der Überlegungen:

  • Welche Bildtypen lassen Sie zu?
  • Wie groß müssen die Bilder sein? Wie wirkt sich das auf die Leistung aus?
  • Welches Seitenverhältnis sollen die Bilder haben?
  • Wie werden die Bilder moderiert? Werden unangemessene Bilder erkannt?
  • Wo werden die Bilder gehostet? Wie wird dies verwaltet?

Serverseitige Tools wie Paperclip und ImageProcessor bieten eine Lösung für die meisten dieser Bedenken. Leider gibt es kein fertiges Tool für eine Single-Page-App (das ich gefunden habe). Ich werde Ihnen zeigen, wie ich dies in einer React-Anwendung gelöst habe, die überhaupt keine serverseitige Sprache verwendet.

Hier ist eine kleine Demo dessen, was wir bauen werden

Toolkit

Die drei Werkzeuge, die ich verwendet habe, sind:

Cloudinary einrichten

Cloudinary ist ein cloudbasierter Dienst, bei dem Sie Bilder speichern, bearbeiten, verwalten und ausliefern können. Ich habe mich für Cloudinary entschieden, da es eine kostenlose Stufe hat, die alle benötigten Funktionen umfasst. Sie benötigen mindestens ein kostenloses Konto, um loszulegen.

Angenommen, Sie möchten hochgeladene Bilder zuschneiden, skalieren und einen Filter hinzufügen. Cloudinary hat das Konzept von Transformationen, die miteinander verkettet werden, um Bilder nach Bedarf zu ändern. Nach dem Hochladen erfolgen die Transformationen, die das neue Bild ändern und speichern.

Gehen Sie im Cloudinary-Dashboard zu Einstellungen > Upload und wählen Sie unter „Upload-Presets“ die Option „Upload-Preset hinzufügen“.

Ändern Sie auf dem folgenden Bildschirm den „Modus“ auf „Nicht signiert“. Dies ist notwendig, damit Sie direkt nach Cloudinary hochladen können, ohne einen privaten Schlüssel über eine serverseitige Sprache aushandeln zu müssen.

Fügen Sie Transformationen hinzu, indem Sie im Abschnitt „Eingehende Transformationen“ auf „Bearbeiten“ klicken. Hier können Sie zuschneiden, skalieren, Qualität ändern, drehen, filtern usw. Speichern Sie das Preset, und das war's! Sie haben jetzt einen Ort, um Bilder für Ihre App hochzuladen, zu bearbeiten, zu speichern und auszuliefern. Beachten Sie den Preset-Namen, da wir ihn später verwenden werden. Lassen Sie uns zum Code übergehen.

Benutzereingaben entgegennehmen

Zur Abwicklung des Bild-Uploads habe ich react-dropzone verwendet. Es bietet Funktionen wie Drag-and-Drop, Dateitypbeschränkung und den Upload mehrerer Dateien.

Installieren Sie zunächst die Abhängigkeiten. Führen Sie in Ihrer Befehlszeile aus:

npm install react react-dropzone superagent --save

Importieren Sie dann React, react-dropzone und superagent in Ihre Komponente. Ich verwende die ES6 import-Syntax:

import React from 'react';
import Dropzone from 'react-dropzone';
import request from 'superagent';

Wir werden superagent später verwenden. Fügen Sie vorerst in der Render-Methode Ihrer Komponente eine react-dropzone-Instanz ein:

export default class ContactForm extends React.Component {

  render() {
    <Dropzone
      multiple={false}
      accept="image/*"
      onDrop={this.onImageDrop.bind(this)}>
      <p>Drop an image or click to select a file to upload.</p>
    </Dropzone>
  }

Readyer Lucas Recoaro schrieb und sagte, dass der folgende Dropzone-Snippet für ihn besser funktioniert. Es ist wahrscheinlich, dass sich die Syntax in einer neueren Version der Bibliothek geändert hat.

<Dropzone
  onDrop={this.onImageDrop.bind(this)}
  accept="image/*"
  multiple={false}>
    {({getRootProps, getInputProps}) => {
      return (
        <div
          {...getRootProps()}
        >
          <input {...getInputProps()} />
          {
          <p>Try dropping some files here, or click to select files to upload.</p>
          }
        </div>
      )
  }}
</Dropzone>

Hier ist eine Übersicht darüber, was diese Komponente tut:

  • multiple={false} erlaubt nur den gleichzeitigen Upload eines Bildes.
  • accept="image/*" erlaubt jeden Bildtyp. Sie können expliziter sein, um nur bestimmte Dateitypen zuzulassen, z. B. accept="image/jpg,image/png".
  • onDrop ist eine Methode, die ausgelöst wird, wenn ein Bild hochgeladen wird.

Bei der Verwendung der React ES5-Klassensyntax (React.createClass) sind alle Methoden „autobound“ an die Klasseninstanz. Der Code in diesem Beitrag verwendet die ES6-Klassensyntax (extends React.Component), die keine automatische Bindung bietet. Deshalb verwenden wir .bind(this) im onDrop-Prop. (Wenn Sie mit .bind nicht vertraut sind, können Sie hier darüber lesen.

Bild-Drop handhaben

Nun richten wir die Methode ein, um etwas zu tun, wenn ein Bild hochgeladen wird.

Zuerst richten wir eine const für zwei wichtige Upload-Informationen ein:

  1. Die Upload-Preset-ID (automatisch für Sie erstellt, als Sie Ihr Upload-Preset erstellt haben)
  2. Ihre Cloudinary-Upload-URL
// import statements

const CLOUDINARY_UPLOAD_PRESET = 'your_upload_preset_id';
const CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/your_cloudinary_app_name/upload';

export default class ContactForm extends React.Component {
// render()

Fügen Sie dann einen Eintrag zum anfänglichen Zustand der Komponente hinzu (mit this.setState); ich habe ihn uploadedFileCloudinaryUrl genannt. Schließlich wird er eine von Cloudinary erstellte URL eines hochgeladenen Bildes enthalten. Wir werden dieses Stück Zustand später verwenden.

export default class ContactForm extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      uploadedFileCloudinaryUrl: ''
    };
  }

Die react-dropzone-Dokumentation besagt, dass sie immer ein Array der hochgeladenen Datei(en) zurückgibt. Daher übergeben wir dieses Array an den Parameter files der Methode onImageDrop. Da wir nur ein Bild gleichzeitig zulassen, wissen wir, dass sich das Bild immer an der ersten Position im Array befindet.

Rufen Sie handleImageUpload auf und übergeben Sie das Bild (files[0]) an diese Methode. Ich habe dies in eine separate Methode aufgeteilt, um das Prinzip der einzelnen Verantwortung zu befolgen. Im Wesentlichen lehrt Sie dieses Prinzip, Methoden kompakt zu halten und nur eine Sache zu tun.

export default class ContactForm extends React.Component {

  constructor(props) { ... }

  onImageDrop(files) {
    this.setState({
      uploadedFile: files[0]
    });

    this.handleImageUpload(files[0]);
  }

  render() { ... }

}

Bild-Upload und -Übertragung verarbeiten

Verwenden Sie zuerst superagent, um mittels POST an Cloudinary zu senden, unter Verwendung der beiden zuvor eingerichteten const. Die Verwendung der .field Methode gibt uns die Möglichkeit, Daten an die POST-Anfrage anzuhängen. Diese Daten enthalten alle Informationen, die Cloudinary benötigt, um das hochgeladene Bild zu verarbeiten. Durch den Aufruf von .end wird die Anfrage ausgeführt und ein Callback bereitgestellt.

export default class ContactForm extends React.Component {

  constructor(props) { ... }

  onImageDrop(files) { ... }

  handleImageUpload(file) {
    let upload = request.post(CLOUDINARY_UPLOAD_URL)
                        .field('upload_preset', CLOUDINARY_UPLOAD_PRESET)
                        .field('file', file);

    upload.end((err, response) => {
      if (err) {
        console.error(err);
      }

      if (response.body.secure_url !== '') {
        this.setState({
          uploadedFileCloudinaryUrl: response.body.secure_url
        });
      }
    });
  }

  render() { ... }

}

Innerhalb des .end-Callbacks protokolliere ich alle zurückgegebenen Fehler. Es ist wahrscheinlich am besten, dem Benutzer ebenfalls mitzuteilen, dass ein Fehler aufgetreten ist.

Als Nächstes prüfen wir, ob die erhaltene Antwort eine URL enthält, die keine leere Zeichenkette ist. Das bedeutet, dass das Bild hochgeladen und manipuliert wurde und Cloudinary eine URL generiert hat. Wenn ein Benutzer beispielsweise sein Profil bearbeiten und ein Bild hochladen würde, könnten Sie die neue Bild-URL von Cloudinary in Ihrer Datenbank speichern.

Mit dem bisher geschriebenen Code kann ein Benutzer ein Bild fallen lassen, die Komponente sendet es an Cloudinary und empfängt eine transformierte Bild-URL, die wir verwenden können.

Render, fortgesetzt

Der letzte Teil der Komponente ist ein div, das eine Vorschau des hochgeladenen Bildes enthält.

export default class ContactForm extends React.Component {

  constructor(props) { ... }

  onImageDrop(files) { ... }

  handleImageUpload(file) { ... }

  render() {
    <div>
      <div className="FileUpload">
        ...
      </div>

      <div>
        {this.state.uploadedFileCloudinaryUrl === '' ? null :
        <div>
          <p>{this.state.uploadedFile.name}</p>
          <img src={this.state.uploadedFileCloudinaryUrl} />
        </div>}
      </div>
    </div>
  }

Der ternäre Operator gibt null (nichts) aus, wenn der uploadedFileCloudinaryUrl-Zustand eine leere Zeichenkette ist. Denken Sie daran, dass wir standardmäßig den uploadedFileCloudinaryUrl-Zustand der Komponente auf eine leere Zeichenkette setzen. Das bedeutet, dass dieses div leer ist, wenn die Komponente gerendert wird.

Wenn Cloudinary jedoch mit einer URL antwortet, ist der Zustand keine leere Zeichenkette mehr, da wir den Zustand in handleImageUpload aktualisiert haben. Zu diesem Zeitpunkt wird die Komponente neu gerendert und zeigt den Namen der hochgeladenen Datei und eine Vorschau des transformierten Bildes an.

Zusammenfassung

Dies ist nur das Fundament für eine Bild-Upload-Komponente. Es gibt viele zusätzliche Funktionen, die Sie hinzufügen könnten, wie zum Beispiel:

  • Hochladen mehrerer Bilder zulassen
  • Entfernen hochgeladener Bilder
  • Fehler anzeigen, wenn der Upload aus irgendeinem Grund fehlschlägt
  • Verwendung der Kamera eines Mobilgeräts als Upload-Quelle

Bisher hat diese Einrichtung für meine Bedürfnisse gut funktioniert. Das harte Kodieren des Upload-Presets ist nicht ideal, aber ich hatte damit noch keine Probleme.

Ich hoffe, Sie haben ein Verständnis dafür bekommen, wie Sie Bilder mit React ohne serverseitige Sprache hochladen, speichern und bearbeiten können. Wenn Sie Fragen oder Anmerkungen haben, höre ich sie gerne! Ich habe ein Repository erstellt, in dem Sie diesen Code in Aktion sehen können.