Mengonversi Pidato ke PDF dengan NextJS dan ExpressJS PlatoBlockchain Data Intelligence. Pencarian Vertikal. Ai.

Mengonversi Ucapan ke PDF dengan NextJS dan ExpressJS

Dengan antarmuka ucapan menjadi lebih penting, ada baiknya menjelajahi beberapa hal yang dapat kita lakukan dengan interaksi ucapan. Seperti, bagaimana jika kita bisa mengatakan sesuatu dan itu ditranskripsi dan dipompa keluar sebagai PDF yang dapat diunduh?

Nah, peringatan spoiler: kami benar-benar bisa lakukan itu! Ada perpustakaan dan kerangka kerja yang dapat kita buat bersama untuk mewujudkannya, dan itulah yang akan kita lakukan bersama dalam artikel ini.

Ini adalah alat yang kami gunakan

Pertama, ini adalah dua pemain besar: Next.js dan Express.js.

Next.js tacks pada fungsionalitas tambahan untuk React, termasuk fitur utama untuk membangun situs statis. Ini adalah pilihan bagi banyak pengembang karena apa yang ditawarkannya langsung, seperti perutean dinamis, pengoptimalan gambar, perutean domain dan subdomain bawaan, penyegaran cepat, perutean sistem file, dan rute APIโ€ฆ banyak, banyak hal lainnya.

Dalam kasus kami, kami pasti membutuhkan Next.js untuknya Rute API di server klien kami. Kami menginginkan rute yang mengambil file teks, mengubahnya menjadi PDF, menulisnya ke sistem file kami, lalu mengirimkan respons ke klien.

Ekspres.js memungkinkan kita untuk menjalankan aplikasi Node.js kecil dengan perutean, pembantu HTTP, dan templating. Ini adalah server untuk API kami sendiri, yang akan kami perlukan saat kami meneruskan dan mengurai data di antara berbagai hal.

Kami memiliki beberapa dependensi lain yang akan kami gunakan:

  1. reaksi-ucapan-pengakuan: Pustaka untuk mengonversi ucapan menjadi teks, membuatnya tersedia untuk komponen Bereaksi.
  2. waktu kerja regenerator: Pustaka untuk memecahkan masalah โ€œregeneratorRuntime tidak ditentukanโ€ kesalahan yang muncul di Next.js saat menggunakan pengakuan-ucapan-reaksi
  3. html-pdf-simpul: Pustaka untuk mengonversi halaman HTML atau URL publik menjadi PDF
  4. aksioma: Pustaka untuk membuat permintaan HTTP di browser dan Node.js
  5. kor: Pustaka yang memungkinkan berbagi sumber daya lintas-asal

Menyiapkan

Hal pertama yang ingin kita lakukan adalah membuat dua folder proyek, satu untuk klien dan satu untuk server. Beri nama mereka apa pun yang Anda suka. Saya menamai milik saya audio-to-pdf-client dan audio-to-pdf-server, Masing-masing.

Cara tercepat untuk memulai dengan Next.js di sisi klien adalah dengan bootstrap dengan buat-aplikasi-berikutnya. Jadi, buka terminal Anda dan jalankan perintah berikut dari folder proyek klien Anda:

npx create-next-app client

Sekarang kami membutuhkan server Express kami. Kita bisa mendapatkannya dengan cd-ing ke folder proyek server dan menjalankan npm init memerintah. A package.json file akan dibuat di folder proyek server setelah selesai.

Kita masih harus benar-benar menginstal Express, jadi mari kita lakukan sekarang dengan npm install express. Sekarang kita bisa membuat yang baru index.js file di folder proyek server dan letakkan kode ini di sana:

const express = require("express")
const app = express()

app.listen(4000, () => console.log("Server is running on port 4000"))

Siap menjalankan server?

node index.js

Kami akan membutuhkan beberapa folder lagi dan file lain untuk melanjutkan:

  • Membuat components folder di folder proyek klien.
  • Membuat SpeechToText.jsx file di components subfolder.

Sebelum kita melangkah lebih jauh, ada sedikit pembersihan yang harus dilakukan. Secara khusus, kita perlu mengganti kode default di pages/index.js berkas dengan ini:

import Head from "next/head";
import SpeechToText from "../components/SpeechToText";

export default function Home() {
  return (
    <div className="home">
      <Head>
        <title>Audio To PDF</title>
        <meta
          name="description"
          content="An app that converts audio to pdf in the browser"
        />
        <link rel="icon" href="/id/favicon.ico" />
      </Head>

      <h1>Convert your speech to pdf</h1>

      <main>
        <SpeechToText />
      </main>
    </div>
  );
}

yang diimpor SpeechToText komponen pada akhirnya akan diekspor dari components/SpeechToText.jsx.

Mari kita instal dependensi lainnya

Baiklah, kami memiliki pengaturan awal untuk aplikasi kami. Sekarang kita dapat menginstal perpustakaan yang menangani data yang diedarkan.

Kami dapat menginstal dependensi klien kami dengan:

npm install react-speech-recognition regenerator-runtime axios

Ketergantungan server Express kami ada di urutan berikutnya, jadi mari cd ke dalam folder proyek server dan instal itu:

npm install html-pdf-node cors

Mungkin saat yang tepat untuk berhenti sejenak dan memastikan file di folder proyek kami dalam keadaan baik. Inilah yang harus Anda miliki di folder proyek klien saat ini:

/audio-to-pdf-web-client
โ”œโ”€ /components
|  โ””โ”€โ”€ SpeechToText.jsx
โ”œโ”€ /pages
|  โ”œโ”€ _app.js
|  โ””โ”€โ”€ index.js
โ””โ”€โ”€ /styles
    โ”œโ”€globals.css
    โ””โ”€โ”€ Home.module.css

Dan inilah yang harus Anda miliki di folder proyek server:

/audio-to-pdf-server
โ””โ”€โ”€ index.js

Membangun UI

Yah, pidato-ke-PDF kita tidak akan terlalu bagus jika tidak ada cara untuk berinteraksi dengannya, jadi mari kita buat komponen React untuk itu yang bisa kita panggil <SpeechToText>.

Anda benar-benar dapat menggunakan markup Anda sendiri. Inilah yang harus saya berikan kepada Anda gambaran tentang bagian-bagian yang kami susun:

import React from "react";

const SpeechToText = () => {
  return (
    <>
      <section>
        <div className="button-container">
          <button type="button" style={{ "--bgColor": "blue" }}>
            Start
          </button>
          <button type="button" style={{ "--bgColor": "orange" }}>
            Stop
          </button>
        </div>
        <div
          className="words"
          contentEditable
          suppressContentEditableWarning={true}
        ></div>
        <div className="button-container">
          <button type="button" style={{ "--bgColor": "red" }}>
            Reset
          </button>
          <button type="button" style={{ "--bgColor": "green" }}>
            Convert to pdf
          </button>
        </div>
      </section>
    </>
  );
};

export default SpeechToText;

Komponen ini mengembalikan Fragmen reaksi yang berisi HTML <``section``> elemen yang berisi tiga div:

  • .button-container berisi dua tombol yang akan digunakan untuk memulai dan menghentikan pengenalan suara.
  • .words memiliki contentEditable dan suppressContentEditableWarning atribut untuk membuat elemen ini dapat diedit dan menekan peringatan apa pun dari React.
  • Lain .button-container memegang dua tombol lagi yang akan digunakan untuk mengatur ulang dan mengonversi ucapan ke PDF, masing-masing.

Styling adalah hal lain sama sekali. Saya tidak akan membahasnya di sini, tetapi Anda dipersilakan untuk menggunakan beberapa gaya yang saya tulis sebagai titik awal untuk gaya Anda sendiri. styles/global.css file.

Lihat CSS Lengkap
html,
body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}

a {
  color: inherit;
  text-decoration: none;
}

* {
  box-sizing: border-box;
}

.home {
  background-color: #333;
  min-height: 100%;
  padding: 0 1rem;
  padding-bottom: 3rem;
}

h1 {
  width: 100%;
  max-width: 400px;
  margin: auto;
  padding: 2rem 0;
  text-align: center;
  text-transform: capitalize;
  color: white;
  font-size: 1rem;
}

.button-container {
  text-align: center;
  display: flex;
  justify-content: center;
  gap: 3rem;
}

button {
  color: white;
  background-color: var(--bgColor);
  font-size: 1.2rem;
  padding: 0.5rem 1.5rem;
  border: none;
  border-radius: 20px;
  cursor: pointer;
}

button:hover {
  opacity: 0.9;
}

button:active {
  transform: scale(0.99);
}

.words {
  max-width: 700px;
  margin: 50px auto;
  height: 50vh;
  border-radius: 5px;
  padding: 1rem 2rem 1rem 5rem;
  background-image: -webkit-gradient(
    linear,
    0 0,
    0 100%,
    from(#d9eaf3),
    color-stop(4%, #fff)
  ) 0 4px;
  background-size: 100% 3rem;
  background-attachment: scroll;
  position: relative;
  line-height: 3rem;
  overflow-y: auto;
}

.success,
.error {
  background-color: #fff;
  margin: 1rem auto;
  padding: 0.5rem 1rem;
  border-radius: 5px;
  width: max-content;
  text-align: center;
  display: block;
}

.success {
  color: green;
}

.error {
  color: red;
}

Variabel CSS di sana digunakan untuk mengontrol warna latar belakang tombol.

Mari kita lihat perubahan terbaru! Lari npm run dev di terminal dan periksa.

Anda akan melihat ini di browser ketika Anda mengunjungi http://localhost:3000:

Mengonversi Ucapan ke PDF dengan NextJS dan ExpressJS

Konversi pidato ke teks pertama kami!

Tindakan pertama yang harus diambil adalah mengimpor dependensi yang diperlukan ke dalam <SpeechToText> komponen:

import React, { useRef, useState } from "react";
import SpeechRecognition, {
  useSpeechRecognition,
} from "react-speech-recognition";
import axios from "axios";

Kemudian kami memeriksa apakah pengenalan ucapan didukung oleh browser dan memberikan pemberitahuan jika tidak didukung:

const speechRecognitionSupported =
  SpeechRecognition.browserSupportsSpeechRecognition();

if (!speechRecognitionSupported) {
  return <div>Your browser does not support speech recognition.</div>;
}

Selanjutnya, mari kita ekstrak transcript dan resetTranscript dari useSpeechRecognition() kait:

const { transcript, resetTranscript } = useSpeechRecognition();

Inilah yang kita butuhkan untuk negara yang menangani listening:

const [listening, setListening] = useState(false);

Kami juga membutuhkan ref untuk div pada pengatur terkenal. Pengatur ini menawarkan bantuan hukum kepada traderapabila trader berselisih dengan broker yang terdaftar dengan mereka. contentEditable atribut, maka kita perlu menambahkan ref atribut untuk itu dan lulus transcript as children:

const textBodyRef = useRef(null);

โ€ฆdan:

<div
  className="words"
  contentEditable
  ref={textBodyRef}
  suppressContentEditableWarning={true}
  >
  {transcript}
</div>

Hal terakhir yang kita butuhkan di sini adalah fungsi yang memicu pengenalan suara dan untuk mengikat fungsi itu ke onClick pendengar acara tombol kami. Tombol mengatur mendengarkan true dan membuatnya berjalan terus menerus. Kami akan menonaktifkan tombol saat dalam keadaan itu untuk mencegah kami meluncurkan acara tambahan.

const startListening = () => {
  setListening(true);
  SpeechRecognition.startListening({
    continuous: true,
  });
};

โ€ฆdan:

<button
  type="button"
  onClick={startListening}
  style={{ "--bgColor": "blue" }}
  disabled={listening}
>
  Start
</button>

Mengklik tombol sekarang harus memulai transkripsi.

Lebih banyak fungsi

Oke, jadi kita punya komponen yang bisa awal mendengarkan. Tapi sekarang kita membutuhkannya untuk melakukan beberapa hal lain juga, seperti stopListening, resetText dan handleConversion. Mari kita buat fungsi-fungsi itu.

const stopListening = () => {
  setListening(false);
  SpeechRecognition.stopListening();
};

const resetText = () => {
  stopListening();
  resetTranscript();
  textBodyRef.current.innerText = "";
};

const handleConversion = async () => {}

Setiap fungsi akan ditambahkan ke onClick pendengar acara pada tombol yang sesuai:

<button
  type="button"
  onClick={stopListening}
  style={{ "--bgColor": "orange" }}
  disabled={listening === false}
>
  Stop
</button>

<div className="button-container">
  <button
    type="button"
    onClick={resetText}
    style={{ "--bgColor": "red" }}
  >
    Reset
  </button>
  <button
    type="button"
    style={{ "--bgColor": "green" }}
    onClick={handleConversion}
  >
    Convert to pdf
  </button>
</div>

Grafik handleConversion fungsi tidak sinkron karena pada akhirnya kita akan membuat permintaan API. Tombol "Stop" memiliki atribut nonaktif yang akan dipicu saat mendengarkan salah.

Jika kita me-restart server dan me-refresh browser, kita sekarang dapat memulai, menghentikan, dan mengatur ulang transkripsi ucapan kita di browser.

Sekarang yang kita butuhkan adalah aplikasinya menuliskan pidato yang dikenali dengan mengubahnya menjadi file PDF. Untuk itu, kita membutuhkan jalur sisi server dari Express.js.

Menyiapkan rute API

Tujuan rute ini adalah untuk mengambil file teks, mengonversinya menjadi PDF, menulis PDF itu ke sistem file kami, lalu mengirim respons ke klien.

Untuk mengatur, kami akan membuka server/index.js file dan impor html-pdf-node dan fs dependensi yang akan digunakan untuk menulis dan membuka sistem file kita.

const HTMLToPDF = require("html-pdf-node");
const fs = require("fs");
const cors = require("cors)

Selanjutnya, kami akan mengatur rute kami:

app.use(cors())
app.use(express.json())

app.post("/", (req, res) => {
  // etc.
})

Kami kemudian melanjutkan untuk menentukan opsi kami yang diperlukan untuk menggunakan html-pdf-node di dalam rute:

let options = { format: "A4" };
let file = {
  content: `<html><body><pre style='font-size: 1.2rem'>${req.body.text}</pre></body></html>`,
};

Grafik options objek menerima nilai untuk mengatur ukuran dan gaya kertas. Ukuran kertas mengikuti sistem yang jauh berbeda dari unit ukuran yang biasanya kami gunakan di web. Sebagai contoh, A4 adalah ukuran huruf yang khas.

Grafik file objek menerima URL situs web publik atau markup HTML. Untuk menghasilkan halaman HTML kami, kami akan menggunakan html, body, pre Tag HTML dan teks dari req.body.

Anda dapat menerapkan gaya apa pun pilihan Anda.

Selanjutnya, kita akan menambahkan trycatch untuk menangani kesalahan apa pun yang mungkin muncul di sepanjang jalan:

try {

} catch(error){
  console.log(error);
  res.status(500).send(error);
}

Selanjutnya, kita akan menggunakan generatePdf dari html-pdf-node perpustakaan untuk menghasilkan pdfBuffer (file PDF mentah) dari file kami dan buat yang unik pdfName:

HTMLToPDF.generatePdf(file, options).then((pdfBuffer) => {
  // console.log("PDF Buffer:-", pdfBuffer);
  const pdfName = "./data/speech" + Date.now() + ".pdf";

  // Next code here
}

Dari sana, kami menggunakan modul sistem file untuk menulis, membaca, dan (ya, akhirnya!) mengirim respons ke aplikasi klien:

fs.writeFile(pdfName, pdfBuffer, function (writeError) {
  if (writeError) {
    return res
      .status(500)
      .json({ message: "Unable to write file. Try again." });
  }

  fs.readFile(pdfName, function (readError, readData) {
    if (!readError && readData) {
      // console.log({ readData });
      res.setHeader("Content-Type", "application/pdf");
      res.setHeader("Content-Disposition", "attachment");
      res.send(readData);
      return;
    }

    return res
      .status(500)
      .json({ message: "Unable to write file. Try again." });
  });
});

Mari kita uraikan sedikit:

  • Grafik writeFile modul sistem file menerima nama file, data, dan fungsi panggilan balik yang dapat mengembalikan pesan kesalahan jika ada masalah saat menulis ke file. Jika Anda bekerja dengan CDN yang menyediakan titik akhir kesalahan, Anda dapat menggunakannya sebagai gantinya.
  • Grafik readFile modul sistem file menerima nama file dan fungsi panggilan balik yang mampu atau mengembalikan kesalahan baca serta data yang dibaca. Setelah kami tidak memiliki kesalahan baca dan data baca ada, kami akan membuat dan mengirim respons ke klien. Sekali lagi, ini dapat diganti dengan titik akhir CDN Anda jika Anda memilikinya.
  • Grafik res.setHeader("Content-Type", "application/pdf"); memberitahu browser bahwa kami mengirim file PDF.
  • Grafik res.setHeader("Content-Disposition", "attachment"); memberitahu browser untuk membuat data yang diterima dapat diunduh.

Karena rute API sudah siap, kami dapat menggunakannya di aplikasi kami di http://localhost:4000. Kami dapat melanjutkan ke bagian klien dari aplikasi kami untuk menyelesaikan handleConversion fungsi.

Menangani konversi

Sebelum kita bisa mulai mengerjakan a handleConversion fungsi, kita perlu membuat status yang menangani permintaan API kita untuk memuat, kesalahan, sukses, dan pesan lainnya. Kami akan menggunakan React's useState kait untuk mengaturnya:

const [response, setResponse] = useState({
  loading: false,
  message: "",
  error: false,
  success: false,
});

Dalam majalah handleConversion fungsi, kami akan memeriksa kapan halaman web telah dimuat sebelum menjalankan kode kami dan memastikan div pada pengatur terkenal. Pengatur ini menawarkan bantuan hukum kepada traderapabila trader berselisih dengan broker yang terdaftar dengan mereka. editable atribut tidak kosong:

if (typeof window !== "undefined") {
const userText = textBodyRef.current.innerText;
  // console.log(textBodyRef.current.innerText);

  if (!userText) {
    alert("Please speak or write some text.");
    return;
  }
}

Kami melanjutkan dengan membungkus permintaan API terakhir kami di a trycatch, menangani kesalahan apa pun yang mungkin muncul, dan memperbarui status respons:

try {

} catch(error){
  setResponse({
    ...response,
    loading: false,
    error: true,
    message:
      "An unexpected error occurred. Text not converted. Please try again",
    success: false,
  });
}

Selanjutnya, kami menetapkan beberapa nilai untuk status respons dan juga mengatur konfigurasi untuk axios dan buat permintaan posting ke server:

setResponse({
  ...response,
  loading: true,
  message: "",
  error: false,
  success: false,
});
const config = {
  headers: {
    "Content-Type": "application/json",
  },
  responseType: "blob",
};

const res = await axios.post(
  "http://localhost:4000",
  {
    text: textBodyRef.current.innerText,
  },
  config
);

Setelah kami mendapatkan respons yang berhasil, kami mengatur status respons dengan nilai yang sesuai dan menginstruksikan browser untuk mengunduh PDF yang diterima:

setResponse({
  ...response,
  loading: false,
  error: false,
  message:
    "Conversion was successful. Your download will start soon...",
  success: true,
});

// convert the received data to a file
const url = window.URL.createObjectURL(new Blob([res.data]));
// create an anchor element
const link = document.createElement("a");
// set the href of the created anchor element
link.href = url;
// add the download attribute, give the downloaded file a name
link.setAttribute("download", "yourfile.pdf");
// add the created anchor tag to the DOM
document.body.appendChild(link);
// force a click on the link to start a simulated download
link.click();

Dan kita dapat menggunakan yang berikut di bawah contentEditable div untuk menampilkan pesan:

<div>
  {response.success && <i className="success">{response.message}</i>}
  {response.error && <i className="error">{response.message}</i>}
</div>

Kode akhir

Saya telah mengemas semuanya di GitHub sehingga Anda dapat memeriksa kode sumber lengkap untuk server dan klien.

Stempel Waktu:

Lebih dari Trik CSS