简体   繁体   English

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

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

Project Details :项目详情

I have a full stack project using ReactJS for the View, NodeJS for back-end and MySQL for database.我有一个完整的堆栈项目,使用 ReactJS 作为视图,使用 NodeJS 作为后端,使用 MySQL 作为数据库。 On my webserver, I'm using NGINX for the web server, and PM2 as a reverse-proxy to run my node instance.在我的网络服务器上,我将 NGINX 用于 web 服务器,并将 PM2 作为反向代理来运行我的节点实例。

The Objective :目标

What I'm trying to do is basically have a user select a StartDate and EndDate from the inputs.我要做的基本上是让用户 select 从输入中获取 StartDate 和 EndDate 。 This will pull data from the MYSQL database and then download a PDF in the browser to the user which will display it in separate PDF's.这将从 MYSQL 数据库中提取数据,然后在浏览器中下载 PDF 给用户,用户将在单独的 PDF 中显示它。 So for example, if the record contained 30 results, it would be one PDF with 30 separate pages.因此,例如,如果记录包含 30 个结果,则它将是一个具有 30 个单独页面的 PDF。

The Problem:问题:

I got all this to work fine on my localhost where it downloads the PDF successfully in the browser to the user but when I push it to a staging or production server it doesn't download the PDF in the browser at all and I'm not sure why.我让这一切在我的本地主机上正常工作,它在浏览器中成功地将 PDF 下载给用户,但是当我将它推送到登台或生产服务器时,它根本不会在浏览器中下载 PDF 而我没有确定为什么。

On staging and live throw the same error in the console.log:在登台和直播时在 console.log 中抛出相同的错误:

在此处输入图像描述

On staging here is a screenshot of the network request.这里暂存的是网络请求的屏幕截图。 In the response there is no response at all that I get back it's just blank but on localhost I do get a response:在响应中根本没有响应,我回来它只是空白,但在本地主机上我确实得到了响应:

However, on localhost the the PDF gets generated successfully and is downloaded in the browser no problem when the user clicks "Generate PDF" button.但是,在本地主机上,当用户单击“生成 PDF”按钮时,PDF 成功生成并在浏览器中下载没有问题。 Here is a screenshot of the network request and the response it gets back:这是网络请求及其返回的响应的屏幕截图:

I have a ton of code so I'll try to show only the relevant problem:我有大量代码,所以我将尝试仅显示相关问题:

View (ReactJS):查看(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: (This is what creates the PDF what it looks like) 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;

Routes路线

 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;

Service:服务:

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

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

As I understand correctly /pdfTemplate?t=xyz returns the pdf's path.据我正确理解 /pdfTemplate?t=xyz 返回 pdf 的路径。

You need to make sure that the pdf is on the server (../web/uploads/file.pdf) and not on your local directory (C://docs/file.pdf)您需要确保 pdf 在服务器上 (../web/uploads/file.pdf) 而不是在本地目录 (C://docs/file.pdf)

Another solution另一种解决方案

  • instead of returning the path, return an encoded base64 string of your pdf;而不是返回路径,而是返回 pdf 的编码 base64 字符串;
  • on your View decode the result在您的查看解码结果
  • transform it into a byte array and create a Blob object from it将其转换为字节数组并从中创建一个 Blob object
  • create a link from this object and open it从此 object 创建一个链接并打开它

Example:例子:

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