NextJS ve ExpressJS PlatoBlockchain Veri Zekası ile Konuşmayı PDF'ye Dönüştürme. Dikey Arama. Ai.

NextJS ve ExpressJS ile Konuşmayı PDF'ye Dönüştürme

Konuşma arayüzlerinin daha çok bir şey haline gelmesiyle, konuşma etkileşimleriyle yapabileceğimiz bazı şeyleri keşfetmeye değer. Mesela, bir şey söyleyip bunu yazıya döküp indirilebilir bir PDF olarak yayınlasak nasıl olur?

Pekala, spoiler uyarısı: biz kesinlikle yapabilmek yap bunu! Bunu gerçekleştirmek için bir araya getirebileceğimiz kütüphaneler ve çerçeveler var ve bu makalede birlikte yapacağımız şey bu.

Bunlar kullandığımız araçlar

Öncelikle, bunlar iki büyük oyuncu: Next.js ve Express.js.

Next.js Statik siteler oluşturmak için temel özellikler de dahil olmak üzere, React'e ek işlevler üzerinde çalışır. Dinamik yönlendirme, görüntü optimizasyonu, yerleşik alan ve alt alan yönlendirme, hızlı yenilemeler, dosya sistemi yönlendirme ve API yolları gibi kutudan çıktığı anda sunduğu özellikler nedeniyle birçok geliştirici için bir tercihtir. birçok, başka birçok şey.

Bizim durumumuzda, bunun için kesinlikle Next.js'ye ihtiyacımız var. API yolları istemci sunucumuzda. Bir metin dosyası alan, onu PDF'ye dönüştüren, onu dosya sistemimize yazan ve ardından istemciye bir yanıt gönderen bir yol istiyoruz.

Express.js yönlendirme, HTTP yardımcıları ve şablonlama ile çalışan küçük bir Node.js uygulaması edinmemizi sağlar. Bu, kendi API'miz için bir sunucudur, bu, şeyler arasında veri geçirirken ve ayrıştırırken ihtiyacımız olan şeydir.

Kullanacağımız başka bağımlılıklarımız var:

  1. tepki-konuşma-tanıma: Konuşmayı metne dönüştüren ve onu React bileşenlerinin kullanımına sunan bir kitaplık.
  2. rejeneratör-çalışma zamanı: Sorun giderme için bir kitaplık “regeneratorRuntime tepki-konuşma-tanıma kullanılırken Next.js'de görünen tanımlanmadı" hatası
  3. html-pdf-düğüm: Bir HTML sayfasını veya genel URL'yi PDF'ye dönüştürmek için bir kitaplık
  4. AXIOS: Hem tarayıcıda hem de Node.js'de HTTP istekleri yapmak için bir kitaplık
  5. cors: Kökenler arası kaynak paylaşımına izin veren bir kitaplık

Kurma

Yapmak istediğimiz ilk şey, biri istemci diğeri sunucu için olmak üzere iki proje klasörü oluşturmak. Onlara ne istersen isim ver. benimkine isim veriyorum audio-to-pdf-client ve audio-to-pdf-server, Sırasıyla.

İstemci tarafında Next.js'yi kullanmaya başlamanın en hızlı yolu, onu aşağıdakilerle önyüklemektir: sonraki uygulama oluştur. Bu nedenle, terminalinizi açın ve istemci proje klasörünüzden aşağıdaki komutu çalıştırın:

npx create-next-app client

Şimdi Express sunucumuza ihtiyacımız var. onu alabiliriz cd- sunucu proje klasörüne girerek ve npm init emretmek. A package.json dosya, tamamlandıktan sonra sunucu proje klasöründe oluşturulacaktır.

Hala Express'i gerçekten yüklememiz gerekiyor, o yüzden şimdi bunu npm install express. Artık yeni bir tane oluşturabiliriz index.js sunucu proje klasöründeki dosya ve şu kodu buraya bırakın:

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

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

Sunucuyu çalıştırmaya hazır mısınız?

node index.js

İlerlemek için birkaç klasöre ve bir dosyaya daha ihtiyacımız olacak:

  • Hat için bir components istemci proje klasöründeki klasör.
  • Hat için bir SpeechToText.jsx içindeki dosyayı components alt klasör.

Daha ileri gitmeden önce, yapmamız gereken küçük bir temizlik var. Özellikle, varsayılan kodu değiştirmemiz gerekiyor. pages/index.js bununla dosya:

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="/tr/favicon.ico" />
      </Head>

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

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

İthal SpeechToText bileşen sonunda ihraç edilecek components/SpeechToText.jsx.

Diğer bağımlılıkları yükleyelim

Pekala, uygulamamızın ilk kurulumunu aradan çıkardık. Artık etrafta dolaşan verileri işleyen kitaplıkları kurabiliriz.

İstemci bağımlılıklarımızı aşağıdakilerle kurabiliriz:

npm install react-speech-recognition regenerator-runtime axios

Sırada Ekspres sunucu bağımlılıklarımız var, hadi cd sunucu proje klasörüne atın ve şunları kurun:

npm install html-pdf-node cors

Duraklatmak ve proje klasörlerimizdeki dosyaların eksiksiz olduğundan emin olmak için muhtemelen iyi bir zaman. Bu noktada istemci proje klasöründe olması gerekenler:

/audio-to-pdf-web-client
├─ /components
|  └── SpeechToText.jsx
├─ /pages
|  ├─ _app.js
|  └── index.js
└── /styles
    ├─globals.css
    └── Home.module.css

Ve sunucu proje klasöründe olması gerekenler:

/audio-to-pdf-server
└── index.js

Kullanıcı Arayüzü Oluşturma

Pekala, eğer onunla etkileşime geçmenin bir yolu yoksa, konuşmadan PDF'e o kadar da harika olmazdı, o yüzden arayabileceğimiz bir React bileşeni yapalım. <SpeechToText>.

Tamamen kendi işaretlemenizi kullanabilirsiniz. İşte size bir araya getirdiğimiz parçalar hakkında bir fikir vermem gerekenler:

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;

Bu bileşen, bir Tepki fragmanı HTML içeren <``section``> üç div içeren öğe:

  • .button-container konuşma tanımayı başlatmak ve durdurmak için kullanılacak iki düğme içerir.
  • .words vardır contentEditable ve suppressContentEditableWarning Bu öğeyi düzenlenebilir kılmak ve React'ten gelen uyarıları bastırmak için öznitelikler.
  • Başka .button-container sırasıyla konuşmayı sıfırlamak ve PDF'ye dönüştürmek için kullanılacak iki düğmeyi daha tutar.

Stil tamamen başka bir şeydir. Burada buna girmeyeceğim, ancak yazdığım bazı stilleri kendi tarzınız için bir başlangıç ​​noktası olarak kullanabilirsiniz. styles/global.css dosyası.

Tam CSS'yi Görüntüle
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;
}

Buradaki CSS değişkenleri, düğmelerin arka plan rengini kontrol etmek için kullanılıyor.

En son değişiklikleri görelim! Koşmak npm run dev terminalde ve kontrol edin.

Bunu ziyaret ettiğinizde tarayıcıda görmelisiniz http://localhost:3000:

NextJS ve ExpressJS ile Konuşmayı PDF'ye Dönüştürme

Metne dönüştürmeye ilk konuşmamız!

Yapılacak ilk işlem, gerekli bağımlılıkları programımıza aktarmaktır. <SpeechToText> bileşen:

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

Ardından tarayıcı tarafından konuşma tanımanın desteklenip desteklenmediğini kontrol ediyoruz ve desteklenmiyorsa bir bildirimde bulunuyoruz:

const speechRecognitionSupported =
  SpeechRecognition.browserSupportsSpeechRecognition();

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

Sıradaki, çıkaralım transcript ve resetTranscript itibaren useSpeechRecognition() kanca:

const { transcript, resetTranscript } = useSpeechRecognition();

Yöneten devlet için ihtiyacımız olan şey bu. listening:

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

Ayrıca bir ihtiyacımız var ref için div ile contentEditable öznitelik, sonra eklememiz gerekir ref ona atfet ve geç transcript as children:

const textBodyRef = useRef(null);

…ve:

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

Burada ihtiyacımız olan son şey, konuşma tanımayı tetikleyen ve bu işlevi ana işleve bağlayan bir işlevdir. onClick düğmemizin olay dinleyicisi. Düğme dinlemeyi ayarlar true ve sürekli çalışmasını sağlar. Ek olayları tetiklememizi önlemek için bu durumdayken düğmeyi devre dışı bırakacağız.

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

…ve:

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

Düğmeye tıklamak şimdi transkripsiyona başlamalıdır.

Diğer işlevler

Tamam, yani yapabilecek bir bileşenimiz var başlama dinleme. Ama şimdi birkaç şey daha yapmasına ihtiyacımız var, örneğin stopListening, resetText ve handleConversion. Bu fonksiyonları yapalım.

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

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

const handleConversion = async () => {}

İşlevlerin her biri bir onClick uygun düğmelerde olay dinleyicisi:

<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>

The handleConversion işlev eşzamansızdır çünkü sonunda bir API isteğinde bulunacağız. “Durdur” düğmesi, dinleme yanlış olduğunda tetiklenecek olan devre dışı bırakılmış özniteliğe sahiptir.

Sunucuyu yeniden başlatıp tarayıcıyı yenilersek, artık tarayıcıdaki konuşma transkripsiyonumuzu başlatabilir, durdurabilir ve sıfırlayabiliriz.

Şimdi ihtiyacımız olan şey, uygulamanın uyarlamak bu konuşmayı bir PDF dosyasına dönüştürerek tanıdı. Bunun için Express.js'den sunucu tarafı yoluna ihtiyacımız var.

API rotasını ayarlama

Bu rotanın amacı bir metin dosyası almak, onu PDF'ye dönüştürmek, bu PDF'yi dosya sistemimize yazmak ve ardından istemciye bir yanıt göndermektir.

Kurulum için, server/index.js dosya ve içe aktar html-pdf-node ve fs dosya sistemimizi yazmak ve açmak için kullanılacak bağımlılıklar.

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

Ardından, rotamızı oluşturacağız:

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

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

Daha sonra kullanmak için gerekli seçeneklerimizi tanımlamaya devam ediyoruz. html-pdf-node rota içinde:

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

The options nesne, kağıt boyutunu ve stilini ayarlamak için bir değer kabul eder. Kağıt boyutları, genellikle web'de kullandığımız boyutlandırma birimlerinden çok farklı bir sistem izler. Örneğin, A4 tipik harf boyutudur.

The file nesne, genel bir web sitesinin URL'sini veya HTML işaretlemesini kabul eder. HTML sayfamızı oluşturmak için kullanacağız html, body, pre HTML etiketleri ve metinden req.body.

Seçtiğiniz herhangi bir stili uygulayabilirsiniz.

Ardından, bir ekleyeceğiz trycatch yol boyunca ortaya çıkabilecek hataları işlemek için:

try {

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

Daha sonra, kullanacağız generatePdf itibaren html-pdf-node oluşturmak için kitaplık pdfBuffer (ham PDF dosyası) dosyamızdan ve benzersiz bir pdfName:

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

  // Next code here
}

Oradan, istemci uygulamasına bir yanıt yazmak, okumak ve (evet, sonunda!) göndermek için dosya sistemi modülünü kullanırız:

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." });
  });
});

Bunu biraz parçalayalım:

  • The writeFile dosya sistemi modülü bir dosya adı, veri ve dosyaya yazarken bir sorun olduğunda bir hata mesajı döndürebilen bir geri arama işlevini kabul eder. Hata bitiş noktaları sağlayan bir CDN ile çalışıyorsanız, bunun yerine bunları kullanabilirsiniz.
  • The readFile dosya sistemi modülü, bir dosya adı ve okuma verilerinin yanı sıra bir okuma hatası verebilen veya döndürebilen bir geri arama işlevini kabul eder. Okuma hatamız olmadığında ve okunan veriler mevcut olduğunda, istemciye bir yanıt oluşturup göndereceğiz. Yine, bu, varsa CDN'nizin uç noktalarıyla değiştirilebilir.
  • The res.setHeader("Content-Type", "application/pdf"); tarayıcıya bir PDF dosyası gönderdiğimizi söyler.
  • The res.setHeader("Content-Disposition", "attachment"); tarayıcıya alınan verileri indirilebilir hale getirmesini söyler.

API rotası hazır olduğundan, uygulamamızda şu adreste kullanabiliriz: http://localhost:4000. İşlemi tamamlamak için uygulamamızın istemci kısmına geçebiliriz. handleConversion fonksiyonu.

Dönüşümü işlemek

Bir üzerinde çalışmaya başlamadan önce handleConversion yükleme, hata, başarı ve diğer mesajlar için API isteklerimizi işleyen bir durum oluşturmamız gerekiyor. React'i kullanacağız useState bunu ayarlamak için kanca:

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

içinde handleConversion fonksiyonu, kodumuzu çalıştırmadan önce web sayfasının ne zaman yüklendiğini kontrol edeceğiz ve div ile editable nitelik boş değil:

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

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

Nihai API isteğimizi bir trycatch, ortaya çıkabilecek herhangi bir hatayı ele alma ve yanıt durumunu güncelleme:

try {

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

Ardından, yanıt durumu için bazı değerler belirledik ve ayrıca axios ve sunucuya bir gönderi isteği yapın:

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
);

Başarılı bir yanıt aldığımızda, yanıt durumunu uygun değerlerle ayarlar ve tarayıcıya alınan PDF'yi indirmesi talimatını veririz:

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();

Ve aşağıdakileri contentEditable'ın altında kullanabiliriz div mesajları görüntülemek için:

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

Son kod

Hem sunucu hem de istemci için tam kaynak kodunu kontrol edebilmeniz için her şeyi GitHub'da paketledim.

Zaman Damgası:

Den fazla CSS Püf Noktaları