繁体   English   中英

HTML-PDF npm 在本地主机上工作,但不在 IP 登台服务器上工作?

[英]HTML-PDF npm working on localhost but not on IP Staging Server?

项目详情

我有一个完整的堆栈项目,使用 ReactJS 作为视图,使用 NodeJS 作为后端,使用 MySQL 作为数据库。 在我的网络服务器上,我将 NGINX 用于 web 服务器,并将 PM2 作为反向代理来运行我的节点实例。

目标

我要做的基本上是让用户 select 从输入中获取 StartDate 和 EndDate 。 这将从 MYSQL 数据库中提取数据,然后在浏览器中下载 PDF 给用户,用户将在单独的 PDF 中显示它。 因此,例如,如果记录包含 30 个结果,则它将是一个具有 30 个单独页面的 PDF。

问题:

我让这一切在我的本地主机上正常工作,它在浏览器中成功地将 PDF 下载给用户,但是当我将它推送到登台或生产服务器时,它根本不会在浏览器中下载 PDF 而我没有确定为什么。

在登台和直播时在 console.log 中抛出相同的错误:

在此处输入图像描述

这里暂存的是网络请求的屏幕截图。 在响应中根本没有响应,我回来它只是空白,但在本地主机上我确实得到了响应:

但是,在本地主机上,当用户单击“生成 PDF”按钮时,PDF 成功生成并在浏览器中下载没有问题。 这是网络请求及其返回的响应的屏幕截图:

我有大量代码,所以我将尝试仅显示相关问题:

查看(ReactJS):

 import React, { useEffect, useState } from "react"; import TextField from "@material-ui/core/TextField"; import Button from "@material-ui/core/Button"; import Moment from "moment"; import AppController from "../../controllers/appController"; import Detailedservice from "../../services/Detailedservice"; const DetailedReport = () => { const [dateValues, handleDateChange] = useState({ startDate: "", endDate: "", }); const [emptyText, setEmpty] = useState(false); const [sucessText, setSuccess] = useState(false); const [loadingState, isLoading] = useState(false); const [scheduleData, setSchedule] = useState({ startDate: "", endDate: "", bathData: [], }); const handleChange = (e) => { handleDateChange({...dateValues, [e.target.name]: e.target.value }); }; const data = { selectedDates: { startDate: dateValues.startDate, endDate: dateValues.endDate, }, requestedData: scheduleData, }; // This is what generates the PDF const viewPdf = await Detailedservice.pdfTemplate(data); // These lines download the PDF in the browser for the user var link = document.createElement("a"); // Location of where the download file is located in link.href = AppController.downloadPDF() + "MM_Report.pdf"; link.target = "_blank"; // Name of download file link.download = "MM_Report.pdf"; link.dispatchEvent(new MouseEvent("click")); }; return ( <div className="detailedreportContainer"> <div className="container"> <div className="row d-flex justify-content-center"> <div className="col-lg-2"> <div className="daterange" style={{ marginTop: "150px" }}> <div className="daterangeCenter"> <TextField id="startDate" label="Start Date" name="startDate" type="date" InputLabelProps={{ shrink: true, }} onChange={handleChange} /> </div> </div> </div> <div className="col-lg-2"> <div className="daterange" style={{ marginTop: "150px" }}> <div className="daterangeCenter d-flex justify-content-center"> <TextField id="endDate" label="End Date" name="endDate" type="date" InputLabelProps={{ shrink: true, }} onChange={handleChange} /> </div> </div> </div> </div> <div className="row d-flex justify-content-center" style={{ marginTop: "20px" }} > <Button variant="contained" color="primary" disabled={.dateValues.startDate ||?dateValues:endDate || emptyText? true: false } onClick={fetchSchedule} > {loadingState: ( <i className={"fas fa-circle-notch fa-spin"} style={{ fontSize; "24px" }} /> ); ( "Generate Report" )} </Button> </div> </div> </div> </div> ); }; export default DetailedReport;

Model:(这是创建 PDF 的原因)

 var db = require("../dbconnection"); var htmltopdf = require("html-pdf"); const path = require("path"); var options = { width: "8.5in", height: "11in", format: "Letter", }; var detailedReport = { getSchedule: function (data, callback) { console.log(data); db.query( "SELECT Subject, StartTime, EndTime, FullName, Resident, CommunicationMethod, Email, Phone, Reason from schedule where DateCreated between? AND? order by StartTime, EndTime asc", [data.startDate, data.endDate], callback ); }, pdfMessage: function (data) { let scheduleData = data.requestedData; let dateonDocument = data.selectedDates; var message = ""; // Looping over all schedules chosen and displaying them in multiple batch pdfs. This is possible using the following class "<div style='page-break-before: always'></div>" which should be used to display data on different pages message += "<html>"; message += "<head>"; message += "<meta charset='utf-8' />"; message += "<style>"; message += "* {"; message += "border: 0;"; message += "margin: 0;"; message += "padding: 0;"; message += "}"; message += "body {"; message += "font-family: cursive, Helvetica, sans-serif;"; message += "width: 100%;"; message += "margin: 30px;"; message += "color: #000;"; message += "}"; message += "#uniqueTable,"; message += "#uniqueTable td,"; message += "#uniqueTable th {"; message += "border: 1px solid #000;"; message += "}"; message += "table {"; message += "border-collapse: collapse;"; message += "width: 90%;"; message += "margin: 10px 0;"; message += "text-align: left;"; message += "}"; message += "td {"; message += "font-size: 20px;"; message += "padding: 10px;"; message += "}"; message += "p {"; message += "padding: 3px 0;"; message += "font-size: 14px;"; message += "}"; message += "img {"; message += "padding-left: 60px;"; message += "padding-bottom: 10px;"; message += "display: block;"; message += "}"; message += "h1 {"; message += "font-size: 18px;"; message += "padding: 10px 0;"; message += "color: #35a768;"; message += "}"; message += "h2 {"; message += "font-size: 18px;"; message += "font-weight: bold;"; message += "color: #000;"; message += "}"; message += "hr {"; message += "border: 1px solid #000;"; message += "width: 90%;"; message += "}"; message += "table th {"; message += "font-size: 16px;"; message += "color: #0094e9;"; message += "font-weight: bold;"; message += "}"; message += "</style>"; message += "</head>"; message += "<body>"; message += "<div style='text-align:center'>"; message += "<h1>Report</h1>"; message += "<p>" + dateonDocument.startDate + " - " + dateonDocument.endDate + "</p>"; message += "</div>"; message += "<div style='page-break-before: always'></div>"; scheduleData.forEach((data) => { message += "<div id='pageContent'>"; message += "<table>"; message += "<tr>"; message += "<td>"; message += "<img"; message += "src='https://amazonaws.com/mmlogomain.jpg'"; message += "style='width: 200px; margin: auto'"; message += "/>"; message += "</td>"; message += "</tr>"; message += "</table>"; message += "<div id='header' style='background-color: #e0f2ff; padding: 20px'>"; message += "<h1 style='color: #000; font-size: 24px'>"; message += "Detailed Report"; message += "</h1>"; message += "</div>"; message += "<div id='subheader' style='background-color: #0094e9; height: 30px'></div>"; message += "<table id='uniqueTable'>"; message += "<tbody>"; message += "<tr>"; message += "<td>"; message += "<h1>Date/Time Scheduled:</h1>"; message += "<p>" + data.StartTime + " - " + data.EndTime + "</p>"; message += "</td>"; message += "</tr>"; message += "<tr>"; message += "<td>"; message += "<h1>Communication Method:</h1>"; message += "<p>" + data.CommunicationMethod + "</p>"; message += "</td>"; message += "</tr>"; message += "</tbody>"; message += "</table>"; message += "<table id='uniqueTable'>"; message += "<tbody>"; message += "<tr>"; message += "<td>"; message += "<h1>Visitor's Full Name:</h1>"; message += "<p>" + data.FullName + "</p>"; message += "</td>"; message += "<td>"; message += "<h1>Resident Requesting to Speak to:</h1>"; message += "<p>" + data.Resident + "</p>"; message += "</td>"; message += "</tr>"; message += "<tr>"; message += "<td>"; message += "<h1>Phone Number:</h1>"; message += "<p>" + data.Phone + "</p>"; message += "</td>"; message += "<td rowspan='2'>"; message += "<h1>Reason for Visit:</h1>"; message += "<p>" + data.Reason + "</p>"; message += "</td>"; message += "</tr>"; message += "<tr>"; message += "<td>"; message += "<h1>Email Address:</h1>"; message += "<p>" + data.Email + "</p>"; message += "</td>"; message += "</tr>"; message += "</tbody>"; message += "</table>"; message += "</div>"; message += "<div style='page-break-before: always'></div>"; }); message += "</body>"; message += "</html>"; return message; }, pdfTemplate: function (data, callback) { let pdfPath = "MM_Report.pdf"; htmltopdf.create(detailedReport.pdfMessage(data), options).toFile( path.join(__dirname, "../public/reports/" + pdfPath), function (err, res) { callback(res); } ); }, }; module.exports = detailedReport;

路线

 var express = require("express"); var router = express.Router(); var detailedReport = require("../models/detailedReport"); router.post("/getSchedule", function (req, res) { detailedReport.getSchedule(req.body, function (err, rows) { if (err) { res.json(err); console.log(err); } else { res.send(rows); } }); }); router.post("/pdfTemplate", function (req, res) { detailedReport.pdfTemplate(req.body, function (err, rows) { if (err) { res.json(err); } else { res.json(rows); } }); }); module.exports = router;

服务:

 import serviceBase from "./serviceBase"; const scheduleService = { getSchedule: (data) => serviceBase.post("/api/getSchedule", data), pdfTemplate: (data) => serviceBase.post("/api/pdfTemplate", data), }; export default scheduleService;

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

据我正确理解 /pdfTemplate?t=xyz 返回 pdf 的路径。

您需要确保 pdf 在服务器上 (../web/uploads/file.pdf) 而不是在本地目录 (C://docs/file.pdf)

另一种解决方案

  • 而不是返回路径,而是返回 pdf 的编码 base64 字符串;
  • 在您的查看解码结果
  • 将其转换为字节数组并从中创建一个 Blob object
  • 从此 object 创建一个链接并打开它

例子:

//content - encoded base64 string
generateFile(content) {
    const byteCharacters = atob(content);
    const byteArrays = [];

    for (let offset = 0; offset < byteCharacters.length; offset += 512) {
        const slice = byteCharacters.slice(offset, offset + 512);

        const byteNumbers = new Array(slice.length);
        for (let i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }

        const byteArray = new Uint8Array(byteNumbers);
        byteArrays.push(byteArray);
    }

    const blob = new Blob(byteArrays, {type: "application/pdf"});

    const link = document.createElement("a");
    link.href = window.URL.createObjectURL(blob);
    link.target = '_black';
    link.click();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM