Mivel a beszédfelületek egyre népszerűbbek, érdemes megvizsgálni néhány dolgot, amit a beszédinterakciókkal tehetünk. Például, mi lenne, ha mondanánk valamit, és azt átíratnánk, és letölthető PDF-ként kipumpálnánk?
Nos, spoiler figyelmeztetés: mi feltétlenül tud csináld! Vannak olyan könyvtárak és keretrendszerek, amelyeket összekövezhetünk, hogy ez megvalósuljon, és ebben a cikkben ezt fogjuk megtenni.
Ezeket az eszközöket használjuk
Először is ez a két nagy szereplő: Next.js és Express.js.
Next.js további funkciókat tartalmaz a Reacthoz, beleértve a statikus helyek építésének kulcsfontosságú funkcióit. Sok fejlesztő számára bevált, mert olyan szolgáltatásokat kínál, mint például a dinamikus útválasztás, a képoptimalizálás, a beépített tartomány- és aldomain-útválasztás, a gyors frissítések, a fájlrendszer-útválasztás és az API-útvonalak… sok-sok más dolog.
Esetünkben mindenképpen Next.js kell hozzá API útvonalak kliens szerverünkön. Olyan útvonalat szeretnénk, amely egy szöveges fájlt átvesz, PDF-be konvertálja, beírja a fájlrendszerünkbe, majd választ küld a kliensnek.
Express.js lehetővé teszi, hogy egy kis Node.js alkalmazást indítsunk útválasztással, HTTP-segédekkel és sablonokkal. Ez egy szerver a saját API-nkhoz, amelyre szükségünk lesz, amikor adatokat továbbítunk és értelmezünk a dolgok között.
Van még néhány függőségünk, amelyeket használni fogunk:
- reagál-beszéd-felismerés: A beszédet szöveggé alakító könyvtár, amely elérhetővé teszi a React összetevők számára.
- regenerátor-futási idő: Egy könyvtár a „
regeneratorRuntime
nincs definiálva” hiba, amely a Next.js fájlban jelenik meg, ha reagál a beszédfelismerésre - html-pdf-csomópont: HTML-oldalak vagy nyilvános URL-címek PDF-formátumba konvertálására szolgáló könyvtár
- Axios: Egy könyvtár HTTP-kérések lebonyolításához mind a böngészőben, mind a Node.js-ben
- Cors: Olyan könyvtár, amely lehetővé teszi a források közötti erőforrások megosztását
Felállítása
Első lépésként két projektmappát szeretnénk létrehozni, egyet a kliensnek és egyet a szervernek. Nevezd el őket, ahogy akarod. Megnevezem az enyémet audio-to-pdf-client
és a audio-to-pdf-server
, Ill.
A Next.js kliensoldali használatának leggyorsabb elindítása a rendszerindítás Create-next-app. Tehát nyissa meg a terminált, és futtassa a következő parancsot az ügyfél projekt mappájából:
npx create-next-app client
Most szükségünk van az Express szerverünkre. Meg tudjuk szerezni cd
-a kiszolgáló projekt mappájába, és futtassa a npm init
parancs. A package.json
A fájl létrejön a kiszolgáló projekt mappájában, miután elkészült.
Még ténylegesen telepítenünk kell az Expresst, úgyhogy most tegyük meg npm install express
. Most létrehozhatunk egy újat index.js
fájlt a szerver projekt mappájába, és dobja be ezt a kódot:
const express = require("express")
const app = express()
app.listen(4000, () => console.log("Server is running on port 4000"))
Készen áll a szerver futtatására?
node index.js
Szükségünk lesz még néhány mappára és egy másik fájlra a továbblépéshez:
- Hozzon létre egy
components
mappát az ügyfél projekt mappájában. - Hozzon létre egy
SpeechToText.jsx
fájl acomponents
almappát.
Mielőtt továbbmennénk, van egy kis takarításunk. Pontosabban, le kell cserélnünk az alapértelmezett kódot a pages/index.js
fájl ezzel:
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="/hu/favicon.ico" />
</Head>
<h1>Convert your speech to pdf</h1>
<main>
<SpeechToText />
</main>
</div>
);
}
Az importált SpeechToText
komponens végül exportálásra kerül components/SpeechToText.jsx
.
Telepítsük a többi függőséget
Rendben, az alkalmazásunk kezdeti beállításai már nincsenek útban. Most már telepíthetjük azokat a könyvtárakat, amelyek kezelik a továbbított adatokat.
Ügyfélfüggőségeinket a következőkkel tudjuk telepíteni:
npm install react-speech-recognition regenerator-runtime axios
Az Express szerver függőségei következnek, szóval lássuk cd
a szerver projekt mappájába, és telepítse ezeket:
npm install html-pdf-node cors
Valószínűleg itt az ideje megállni, és megbizonyosodni arról, hogy a projektmappákban lévő fájlok rendben vannak. A következőnek kell lennie ezen a ponton az ügyfél projekt mappájában:
/audio-to-pdf-web-client
├─ /components
| └── SpeechToText.jsx
├─ /pages
| ├─ _app.js
| └── index.js
└── /styles
├─globals.css
└── Home.module.css
És a következőnek kell lennie a szerver projekt mappájában:
/audio-to-pdf-server
└── index.js
A felhasználói felület építése
Nos, a beszédünk PDF-be nem lenne olyan nagyszerű, ha nincs mód vele interakcióra, ezért készítsünk hozzá egy React összetevőt, amelyet hívhatunk. <SpeechToText>
.
Teljesen használhatja a saját jelölését. Íme, hogy képet adjak az összeállított darabokról:
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;
Ez a komponens visszaadja a Reakció fragmentum amely HTML-t tartalmaz <``section``>
három div elemet tartalmazó elem:
.button-container
két gombot tartalmaz, amelyek a beszédfelismerés elindítására és leállítására szolgálnak..words
több mintcontentEditable
és asuppressContentEditableWarning
attribútumokat, hogy ez az elem szerkeszthető legyen, és elrejtsék a React figyelmeztetéseit.- Másik
.button-container
két további gombot tartalmaz, amelyek a beszéd visszaállítására és PDF-be konvertálására szolgálnak.
A stílus teljesen más dolog. Itt nem megyek bele, de nyugodtan használhatsz néhány általam írt stílust kiindulópontként a sajátodhoz. styles/global.css
fájlt.
Teljes CSS megtekintése
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;
}
Az ott található CSS-változók a gombok háttérszínének szabályozására szolgálnak.
Lássuk a legújabb változásokat! Fuss npm run dev
a terminálban, és nézd meg őket.
Ezt látnia kell a böngészőben, amikor meglátogatja http://localhost:3000
:
Első beszédünk szöveggé konvertálása!
Az első lépés a szükséges függőségek importálása a mi rendszerünkbe <SpeechToText>
összetevő:
import React, { useRef, useState } from "react";
import SpeechRecognition, {
useSpeechRecognition,
} from "react-speech-recognition";
import axios from "axios";
Ezután ellenőrizzük, hogy a böngésző támogatja-e a beszédfelismerést, és értesítést küldünk, ha nem támogatja:
const speechRecognitionSupported =
SpeechRecognition.browserSupportsSpeechRecognition();
if (!speechRecognitionSupported) {
return <div>Your browser does not support speech recognition.</div>;
}
Következő lépésként kivonjuk transcript
és a resetTranscript
tól useSpeechRecognition()
horog:
const { transcript, resetTranscript } = useSpeechRecognition();
Erre van szükségünk annak az államnak, amelyik kezeli listening
:
const [listening, setListening] = useState(false);
Szükségünk van továbbá a ref
az div
a ... val contentEditable
attribútumot, akkor hozzá kell adnunk a ref
tulajdonít neki és adja át transcript
as children
:
const textBodyRef = useRef(null);
…és:
<div
className="words"
contentEditable
ref={textBodyRef}
suppressContentEditableWarning={true}
>
{transcript}
</div>
Az utolsó dolog, amire itt szükségünk van, egy olyan funkció, amely kiváltja a beszédfelismerést, és ezt a funkciót a onClick
gombunk eseményfigyelője. A gomb beállítja a hallgatást true
és folyamatos működésre készteti. Amíg ebben az állapotban van, letiltjuk a gombot, nehogy további eseményeket indítsunk el.
const startListening = () => {
setListening(true);
SpeechRecognition.startListening({
continuous: true,
});
};
…és:
<button
type="button"
onClick={startListening}
style={{ "--bgColor": "blue" }}
disabled={listening}
>
Start
</button>
A gombra kattintva most elindul az átírás.
További funkciók
Rendben, van egy alkatrészünk, ami képes kezdet hallgat. De most szükségünk van néhány más dologra is, mint pl stopListening
, resetText
és a handleConversion
. Készítsük el ezeket a függvényeket.
const stopListening = () => {
setListening(false);
SpeechRecognition.stopListening();
};
const resetText = () => {
stopListening();
resetTranscript();
textBodyRef.current.innerText = "";
};
const handleConversion = async () => {}
Mindegyik funkció hozzáadódik egy onClick
eseményfigyelő a megfelelő gombokon:
<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>
A handleConversion
függvény aszinkron, mert végül API kérést fogunk benyújtani. A „Stop” gombnak van egy letiltott attribútuma, amely akkor aktiválódik, ha a hallás hamis.
Ha újraindítjuk a szervert és frissítjük a böngészőt, akkor most már elindíthatjuk, leállíthatjuk és visszaállíthatjuk a beszédátírásunkat a böngészőben.
Most az alkalmazásra van szükségünk lemásol amely felismerte a beszédet PDF-fájllá alakítva. Ehhez szükségünk van az Express.js szerveroldali elérési útjára.
Az API útvonal beállítása
Ennek az útvonalnak az a célja, hogy vegyünk egy szöveges fájlt, konvertáljuk PDF formátumba, beírjuk azt a fájlrendszerünkbe, majd választ küldünk az ügyfélnek.
A beállításhoz megnyitjuk a server/index.js
fájlt és importálja a html-pdf-node
és a fs
a fájlrendszer írásához és megnyitásához használt függőségek.
const HTMLToPDF = require("html-pdf-node");
const fs = require("fs");
const cors = require("cors)
Ezután felállítjuk az útvonalunkat:
app.use(cors())
app.use(express.json())
app.post("/", (req, res) => {
// etc.
})
Ezután folytatjuk a használathoz szükséges opciók meghatározását html-pdf-node
az útvonalon belül:
let options = { format: "A4" };
let file = {
content: `<html><body><pre style='font-size: 1.2rem'>${req.body.text}</pre></body></html>`,
};
A options
objektum elfogad egy értéket a papír méretének és stílusának beállításához. A papírméretek más rendszert követnek, mint a weben általában használt méretezési egységek. Például, Az A4 a tipikus betűméret.
A file
Az objektum vagy egy nyilvános webhely URL-címét vagy HTML-jelölést fogad el. HTML oldalunk létrehozásához a html
, body
, pre
HTML címkék és a szöveg a req.body
.
Bármilyen stílust alkalmazhat, amelyet választott.
Ezután hozzáadjuk a trycatch
az útközben esetlegesen felbukkanó hibák kezelésére:
try {
} catch(error){
console.log(error);
res.status(500).send(error);
}
Ezután a generatePdf
tól html-pdf-node
könyvtár létrehozásához a pdfBuffer
(a nyers PDF fájl) a fájlunkból, és hozzon létre egy egyedit pdfName
:
HTMLToPDF.generatePdf(file, options).then((pdfBuffer) => {
// console.log("PDF Buffer:-", pdfBuffer);
const pdfName = "./data/speech" + Date.now() + ".pdf";
// Next code here
}
Innentől kezdve a fájlrendszer modul segítségével írunk, olvasunk és (igen, végül!) választ küldünk az ügyfélalkalmazásnak:
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." });
});
});
Ezt bontsuk egy kicsit:
- A
writeFile
A fájlrendszer modul elfogad egy fájlnevet, adatokat és egy visszahívási funkciót, amely hibaüzenetet küldhet, ha probléma adódik a fájl írása során. Ha olyan CDN-nel dolgozik, amely hibavégpontokat biztosít, használhatja ezeket helyette. - A
readFile
A fájlrendszer modul olyan fájlnevet és visszahívási függvényt fogad el, amely képes vagy visszaadja az olvasási hibát, valamint az olvasási adatokat. Ha nincs olvasási hiba, és az olvasott adatok jelen vannak, elkészítjük és választ küldünk az ügyfélnek. Ez ismét lecserélhető a CDN végpontjaira, ha rendelkezik velük. - A
res.setHeader("Content-Type", "application/pdf");
közli a böngészővel, hogy PDF-fájlt küldünk. - A
res.setHeader("Content-Disposition", "attachment");
utasítja a böngészőt, hogy tegye letölthetővé a kapott adatokat.
Mivel az API-útvonal készen áll, használhatjuk az alkalmazásunkban a címen http://localhost:4000
. Folytathatjuk kérelmünk kliens részére történő kitöltését handleConversion
funkciót.
Az átalakítás kezelése
Mielőtt elkezdhetnénk dolgozni a handleConversion
függvényt, létre kell hoznunk egy állapotot, amely kezeli az API-kéréseinket a betöltésre, a hibára, a sikerre és az egyéb üzenetekre. A React-ot fogjuk használni useState
kampó a beállításhoz:
const [response, setResponse] = useState({
loading: false,
message: "",
error: false,
success: false,
});
A handleConversion
funkciót, a kód futtatása előtt ellenőrizzük, hogy a weboldal betöltődött-e, és ellenőrizzük, hogy a div
a ... val editable
Az attribútum nem üres:
if (typeof window !== "undefined") {
const userText = textBodyRef.current.innerText;
// console.log(textBodyRef.current.innerText);
if (!userText) {
alert("Please speak or write some text.");
return;
}
}
Úgy járunk el, hogy esetleges API-kérésünket a trycatch
, az esetlegesen felmerülő hibák kezelése és a válaszállapot frissítése:
try {
} catch(error){
setResponse({
...response,
loading: false,
error: true,
message:
"An unexpected error occurred. Text not converted. Please try again",
success: false,
});
}
Ezután beállítunk néhány értéket a válaszállapothoz, és beállítjuk a konfigurációt is axios
és küldjön egy bejegyzési kérelmet a szervernek:
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
);
Miután megkaptuk a sikeres választ, beállítjuk a válasz állapotát a megfelelő értékekkel, és utasítjuk a böngészőt a kapott PDF letöltésére:
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();
És használhatjuk a következőket a contentEditable alatt div
üzenetek megjelenítéséhez:
<div>
{response.success && <i className="success">{response.message}</i>}
{response.error && <i className="error">{response.message}</i>}
</div>
Végső kód
Mindent összecsomagoltam a GitHubon, így mind a szerver, mind a kliens teljes forráskódját megnézheti.