Chuyển đổi giọng nói sang PDF bằng NextJS và ExpressJS PlatoBlockchain Data Intelligence. Tìm kiếm dọc. Ái.

Chuyển đổi giọng nói sang PDF với NextJS và ExpressJS

Với việc giao diện giọng nói ngày càng trở nên phổ biến, bạn nên khám phá một số điều chúng ta có thể làm với tương tác giọng nói. Giống như, điều gì sẽ xảy ra nếu chúng ta có thể nói điều gì đó và đã phiên âm và bơm ra dưới dạng tệp PDF có thể tải xuống?

Chà, cảnh báo spoiler: chúng tôi hoàn toàn có thể làm điều đó! Có những thư viện và khuôn khổ mà chúng ta có thể kết hợp cùng nhau để biến nó thành hiện thực và đó là những gì chúng ta sẽ cùng nhau làm trong bài viết này.

Đây là những công cụ chúng tôi đang sử dụng

Trước hết, đây là hai người chơi lớn: Next.js và Express.js.

Tiếp theo.js đề cập đến các chức năng bổ sung cho React, bao gồm các tính năng chính để xây dựng các trang web tĩnh. Đây là mục tiêu của nhiều nhà phát triển vì những gì nó cung cấp ngay lập tức, như định tuyến động, tối ưu hóa hình ảnh, định tuyến miền phụ và miền tích hợp, làm mới nhanh, định tuyến hệ thống tệp và các tuyến API… trong số nhiều, nhiều thứ khác.

Trong trường hợp của chúng tôi, chúng tôi chắc chắn cần Next.js cho Các tuyến API trên máy chủ khách hàng của chúng tôi. Chúng tôi muốn một lộ trình lấy một tệp văn bản, chuyển đổi nó sang PDF, ghi nó vào hệ thống tệp của chúng tôi, sau đó gửi phản hồi đến máy khách.

Express.js cho phép chúng tôi có được một ứng dụng Node.js nhỏ với tính năng định tuyến, trình trợ giúp HTTP và tạo khuôn mẫu. Đó là một máy chủ cho API của riêng chúng tôi, đó là thứ chúng tôi cần khi chúng tôi chuyển và phân tích dữ liệu giữa các thứ.

Chúng tôi có một số phụ thuộc khác mà chúng tôi sẽ đưa vào sử dụng:

  1. phản ứng-nhận dạng giọng nói: Một thư viện để chuyển đổi lời nói thành văn bản, cung cấp nó cho các thành phần React.
  2. trình tái tạo-thời gian chạy: Một thư viện để khắc phục sự cố “regeneratorRuntime không được xác định ”lỗi hiển thị trong Next.js khi sử dụng nhận dạng giọng nói phản ứng
  3. html-pdf-nút: Thư viện để chuyển đổi một trang HTML hoặc URL công khai thành PDF
  4. axios: Một thư viện để thực hiện các yêu cầu HTTP trong cả trình duyệt và Node.js
  5. áo lót: Một thư viện cho phép chia sẻ tài nguyên nhiều nguồn gốc

Thiết lập

Điều đầu tiên chúng tôi muốn làm là tạo hai thư mục dự án, một cho máy khách và một cho máy chủ. Đặt tên cho họ bất cứ điều gì bạn muốn. Tôi đang đặt tên của tôi audio-to-pdf-clientaudio-to-pdf-server, Tương ứng.

Cách nhanh nhất để bắt đầu với Next.js ở phía máy khách là khởi động nó bằng tạo ứng dụng tiếp theo. Vì vậy, hãy mở thiết bị đầu cuối của bạn và chạy lệnh sau từ thư mục dự án khách hàng của bạn:

npx create-next-app client

Bây giờ chúng tôi cần máy chủ Express của chúng tôi. Chúng tôi có thể lấy nó bằng cách cd-ing vào thư mục dự án máy chủ và chạy npm init yêu cầu. MỘT package.json tệp sẽ được tạo trong thư mục dự án máy chủ sau khi hoàn tất.

Chúng tôi vẫn cần thực sự cài đặt Express, vì vậy hãy làm điều đó ngay bây giờ với npm install express. Bây giờ chúng ta có thể tạo một index.js tệp trong thư mục dự án máy chủ và thả mã này vào đó:

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

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

Sẵn sàng để chạy máy chủ?

node index.js

Chúng tôi sẽ cần thêm một vài thư mục và một tệp khác để tiếp tục:

  • Tạo ra một components thư mục trong thư mục dự án máy khách.
  • Tạo ra một SpeechToText.jsx tập tin trong components thư mục con.

Trước khi đi xa hơn, chúng ta phải dọn dẹp một chút. Cụ thể, chúng ta cần thay thế mã mặc định trong pages/index.js tệp với cái này:

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

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

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

Nhập khẩu SpeechToText thành phần cuối cùng sẽ được xuất từ components/SpeechToText.jsx.

Hãy cài đặt các phụ thuộc khác

Được rồi, chúng tôi đã thiết lập ban đầu cho ứng dụng của mình. Bây giờ chúng ta có thể cài đặt các thư viện xử lý dữ liệu được truyền xung quanh.

Chúng tôi có thể cài đặt các phụ thuộc máy khách của mình với:

npm install react-speech-recognition regenerator-runtime axios

Sự phụ thuộc vào máy chủ Express của chúng tôi sẽ tiếp theo, vì vậy chúng ta hãy cd vào thư mục dự án máy chủ và cài đặt những:

npm install html-pdf-node cors

Có lẽ đây là thời điểm thích hợp để tạm dừng và đảm bảo rằng các tệp trong thư mục dự án của chúng tôi vẫn nguyên vẹn. Đây là những gì bạn nên có trong thư mục dự án máy khách tại thời điểm này:

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

Và đây là những gì bạn nên có trong thư mục dự án máy chủ:

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

Xây dựng giao diện người dùng

Chà, tính năng chuyển lời nói thành PDF của chúng ta sẽ không tuyệt vời như vậy nếu không có cách nào tương tác với nó, vì vậy hãy tạo một thành phần React cho nó mà chúng ta có thể gọi <SpeechToText>.

Bạn hoàn toàn có thể sử dụng đánh dấu của riêng mình. Đây là những gì tôi phải cung cấp cho bạn ý tưởng về các phần chúng tôi đang ghép lại với nhau:

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;

Thành phần này trả về một Phân đoạn phản ứng có chứa một HTML <``section``> phần tử có chứa ba div:

  • .button-container chứa hai nút sẽ được sử dụng để bắt đầu và dừng nhận dạng giọng nói.
  • .wordscontentEditablesuppressContentEditableWarning để làm cho phần tử này có thể chỉnh sửa và loại bỏ bất kỳ cảnh báo nào từ React.
  • Một .button-container giữ thêm hai nút sẽ được sử dụng để đặt lại và chuyển đổi giọng nói sang PDF, tương ứng.

Tạo kiểu hoàn toàn là một điều khác. Tôi sẽ không đi sâu vào nó ở đây, nhưng bạn có thể sử dụng một số phong cách tôi đã viết làm điểm khởi đầu cho riêng bạn styles/global.css tập tin.

Xem toàn bộ CSS
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;
}

Các biến CSS trong đó đang được sử dụng để kiểm soát màu nền của các nút.

Hãy cùng xem những thay đổi mới nhất! Chạy npm run dev trong thiết bị đầu cuối và kiểm tra chúng.

Bạn sẽ thấy điều này trong trình duyệt khi bạn truy cập http://localhost:3000:

Chuyển đổi giọng nói sang PDF với NextJS và ExpressJS

Bài phát biểu đầu tiên của chúng tôi để chuyển đổi văn bản!

Hành động đầu tiên cần thực hiện là nhập các phụ thuộc cần thiết vào <SpeechToText> thành phần:

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

Sau đó, chúng tôi kiểm tra xem trình duyệt có hỗ trợ nhận dạng giọng nói hay không và đưa ra thông báo nếu không được hỗ trợ:

const speechRecognitionSupported =
  SpeechRecognition.browserSupportsSpeechRecognition();

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

Tiếp theo, hãy giải nén transcriptresetTranscript từ useSpeechRecognition() móc:

const { transcript, resetTranscript } = useSpeechRecognition();

Đây là những gì chúng ta cần cho trạng thái xử lý listening:

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

Chúng tôi cũng cần một ref cho div với contentEditable thì chúng ta cần thêm thuộc tính ref quy cho nó và vượt qua transcript as children:

const textBodyRef = useRef(null);

…và:

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

Điều cuối cùng chúng ta cần ở đây là một chức năng kích hoạt nhận dạng giọng nói và liên kết chức năng đó với onClick trình nghe sự kiện của nút của chúng tôi. Nút thiết lập nghe true và làm cho nó chạy liên tục. Chúng tôi sẽ tắt nút khi nó ở trạng thái đó để ngăn chúng tôi kích hoạt các sự kiện bổ sung.

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

…và:

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

Nhấp vào nút bây giờ sẽ bắt đầu phiên âm.

Thêm chức năng

OK, vì vậy chúng tôi có một thành phần có thể Bắt đầu đang lắng nghe. Nhưng bây giờ chúng ta cũng cần nó để làm một số việc khác, như stopListening, resetTexthandleConversion. Hãy thực hiện các chức năng đó.

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

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

const handleConversion = async () => {}

Mỗi chức năng sẽ được thêm vào một onClick trình nghe sự kiện trên các nút thích hợp:

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

Sản phẩm handleConversion hàm không đồng bộ vì cuối cùng chúng tôi sẽ thực hiện một yêu cầu API. Nút "Dừng" có thuộc tính bị vô hiệu hóa sẽ được kích hoạt khi nghe sai.

Nếu chúng tôi khởi động lại máy chủ và làm mới trình duyệt, bây giờ chúng tôi có thể bắt đầu, dừng và đặt lại bản ghi lời nói của mình trong trình duyệt.

Bây giờ những gì chúng tôi cần là ứng dụng chép lại giọng nói được nhận dạng đó bằng cách chuyển đổi nó thành tệp PDF. Để làm được điều đó, chúng tôi cần đường dẫn phía máy chủ từ Express.js.

Thiết lập tuyến API

Mục đích của lộ trình này là lấy một tệp văn bản, chuyển đổi nó thành PDF, ghi tệp PDF đó vào hệ thống tệp của chúng tôi, sau đó gửi phản hồi cho khách hàng.

Để thiết lập, chúng tôi sẽ mở server/index.js tập tin và nhập html-pdf-nodefs các phụ thuộc sẽ được sử dụng để viết và mở hệ thống tệp của chúng tôi.

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

Tiếp theo, chúng tôi sẽ thiết lập lộ trình của mình:

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

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

Sau đó, chúng tôi tiến hành xác định các tùy chọn cần thiết để sử dụng html-pdf-node bên trong tuyến đường:

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

Sản phẩm options đối tượng chấp nhận một giá trị để đặt kích thước và kiểu giấy. Các khổ giấy tuân theo một hệ thống khác nhiều so với các đơn vị định cỡ mà chúng ta thường sử dụng trên web. Ví dụ, A4 là kích thước thư thông thường.

Sản phẩm file đối tượng chấp nhận URL của một trang web công khai hoặc đánh dấu HTML. Để tạo trang HTML của chúng tôi, chúng tôi sẽ sử dụng html, body, pre Thẻ HTML và văn bản từ req.body.

Bạn có thể áp dụng bất kỳ kiểu dáng nào mà bạn lựa chọn.

Tiếp theo, chúng tôi sẽ thêm một trycatch để xử lý bất kỳ lỗi nào có thể xuất hiện trong quá trình:

try {

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

Tiếp theo, chúng tôi sẽ sử dụng generatePdf từ html-pdf-node thư viện để tạo ra một pdfBuffer (tệp PDF thô) từ tệp của chúng tôi và tạo một pdfName:

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

  // Next code here
}

Từ đó, chúng tôi sử dụng mô-đun hệ thống tệp để viết, đọc và (vâng, cuối cùng!) Gửi phản hồi đến ứng dụng khách:

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

Hãy chia nhỏ điều đó một chút:

  • Sản phẩm writeFile mô-đun hệ thống tệp chấp nhận tên tệp, dữ liệu và hàm gọi lại có thể trả về thông báo lỗi nếu có sự cố khi ghi vào tệp. Nếu bạn đang làm việc với một CDN cung cấp các điểm cuối lỗi, bạn có thể sử dụng chúng để thay thế.
  • Sản phẩm readFile mô-đun hệ thống tệp chấp nhận một tên tệp và một hàm gọi lại có khả năng hoặc trả về lỗi đọc cũng như dữ liệu đã đọc. Khi chúng tôi không có lỗi đọc và dữ liệu đọc hiện có, chúng tôi sẽ xây dựng và gửi phản hồi cho khách hàng. Một lần nữa, điều này có thể được thay thế bằng các điểm cuối CDN của bạn nếu bạn có chúng.
  • Sản phẩm res.setHeader("Content-Type", "application/pdf"); cho trình duyệt biết rằng chúng tôi đang gửi tệp PDF.
  • Sản phẩm res.setHeader("Content-Disposition", "attachment"); yêu cầu trình duyệt làm cho dữ liệu đã nhận có thể tải xuống được.

Vì tuyến API đã sẵn sàng, chúng tôi có thể sử dụng nó trong ứng dụng của mình tại http://localhost:4000. Chúng tôi có thể tiến hành phần ứng dụng của khách hàng để hoàn thành handleConversion chức năng.

Xử lý chuyển đổi

Trước khi chúng tôi có thể bắt đầu làm việc trên handleConversion , chúng tôi cần tạo một trạng thái xử lý các yêu cầu API của chúng tôi để tải, lỗi, thành công và các thông báo khác. Chúng tôi sẽ sử dụng React's useState hook để thiết lập:

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

Trong tạp chí handleConversion , chúng tôi sẽ kiểm tra xem trang web đã được tải khi nào trước khi chạy mã của chúng tôi và đảm bảo div với editable thuộc tính không trống:

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

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

Chúng tôi tiếp tục bằng cách gói yêu cầu API cuối cùng của chúng tôi trong một trycatch, xử lý bất kỳ lỗi nào có thể phát sinh và cập nhật trạng thái phản hồi:

try {

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

Tiếp theo, chúng tôi đặt một số giá trị cho trạng thái phản hồi và cũng đặt cấu hình cho axios và thực hiện một yêu cầu đăng lên máy chủ:

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

Khi chúng tôi đã nhận được phản hồi thành công, chúng tôi đặt trạng thái phản hồi với các giá trị thích hợp và hướng dẫn trình duyệt tải xuống tệp PDF đã nhận:

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

Và chúng ta có thể sử dụng phần sau bên dưới contentEditable div để hiển thị thông báo:

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

Mã cuối cùng

Tôi đã đóng gói mọi thứ trên GitHub để bạn có thể kiểm tra mã nguồn đầy đủ cho cả máy chủ và máy khách.

Dấu thời gian:

Thêm từ Thủ thuật CSS