Stack Overflow 社区您好,
我目前正在开发一个用作认证系统的 Express.js 应用程序。该应用程序旨在根据参与者的电子邮件地址生成 PDF 格式的个性化证书。
问题: 我在启动从服务器到客户端的文件下载时遇到问题。应用程序成功生成了 PDF 证书,但是当我尝试触发下载时,没有任何反应,文件也没有下载。我正在本地主机上运行该应用程序。
app.js
const fs=require("fs")
const moment=require("moment")
const express=require("express")
const PDFDocument=require("pdfkit")
const QRCode = require("qrcode");
const { createCanvas } = require("canvas");
const csvparse = require("csv-parse");
const path = require('path');
let parsedData;
const csvData = fs.readFileSync('./data_file/GDSC_MSCW.csv', 'utf8');
const parseCSV = () => {
return new Promise((resolve, reject) => {
const parser = csvparse.parse(csvData, { columns: true, delimiter: "," });
const data = [];
parser.on("data", (row) => {
data.push(row);
});
parser.on("end", () => {
parsedData = data;
resolve();
});
parser.on("error", (err) => {
reject(err);
});
});
};
parseCSV()
.then(() => {
console.log("CSV parsing completed");
})
.catch((error) => {
console.error("Error parsing CSV:", error);
});
const app = express();
const port = 3000;
console.log(csvData)
const generateCertificate = async (name,link) => {
const doc=new PDFDocument({
layout:"landscape",
size:"A4",
})
const fileName = `${name}.pdf`;
const filePath = path.resolve(fileName);
doc.pipe(fs.createWriteStream(`${name}.pdf`))
doc.image("images/certificate_2.png",0,0,{width:842})
doc.fillColor("#3D3D3D")
doc.font("fonts/OpenSans-Bold.ttf")
doc.fontSize(29).text(name,249,157,{
align:"left",
})
const qrCodeDataURL = await QRCode.toDataURL(link, {
version: 6, // QR code version
});
doc.image(qrCodeDataURL, 738, 497, { width: 70 });
doc.end()
return filePath;
}
app.use(express.static(path.join(__dirname, 'public')));
app.get("/getUserInfo", async (req, res) => {
const userEmail = req.query.email;
console.log("Hello");
const user = parsedData.find((entry) => entry.Email === userEmail);
if (user) {
try {
const filePath = await generateCertificate(user.Name, user.ProfileLink);
res.download(filePath, (err) => {
if (err) {
console.error('Error sending file:', err);
res.status(500).json({ success: false, error: 'Internal Server Error' });
} else {
console.log('Download Completed');
fs.unlinkSync(filePath);
}
});
} catch (error) {
console.error('Error generating certificate:', error);
res.status(500).json({ success: false, error: 'Internal Server Error' });
}
} else {
res.status(404).json({
success: false,
message: "Unable to find participant, please make sure you input the correct email id",
});
}
});
app.get('/', (req, res) => {
const indexPath = path.join(__dirname, 'index.html');
res.sendFile(indexPath);
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GCSJ 2023</title>
<!-- Bootstrap CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css">
<!-- Custom styles -->
<style>
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
#header-image {
width: 100%;
}
#content {
flex-grow: 1;
}
.form-container {
text-align: center;
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
#social-icons {
text-align: center;
margin-top: 20px;
}
#footer {
text-align: center;
padding: 20px;
background-color: #f8f9fa; /* Light background color */
}
</style>
</head>
<body class="container">
<!-- Header Image -->
<img src="header_.png" alt="Header Image">
<div id="content">
<h1 class="text-center mt-4 mb-4">Certification 2023-24</h1>
<!-- Form -->
<div class="form-container">
<form id="userForm">
<div class="form-group">
<label for="email" class="sr-only">Enter your email:</label>
<input type="email" class="form-control form-control-lg" id="email" placeholder="Enter your email" required>
</div>
<button type="button" class="btn btn-primary btn-lg" onclick="getUserInfo()">Generate Certificate</button>
</form>
</div>
<!-- Social Icons -->
<div id="social-icons">
<a href="your-social-link" target="_blank" title="YouTube"><i class="fab fa-youtube fa-2x ml-2" style="color: red;"></i></a>
<a href="your-social-link" target="_blank" title="Twitter"><i class="fab fa-twitter fa-2x ml-2" style="color: rgb(0, 144, 201);"></i></a>
<a href="your-social-link" target="_blank" title="Instagram"><i class="fab fa-instagram fa-2x ml-2" style="color: rgb(206, 45, 72);"></i></a>
<a href="your-social-link" target="_blank" title="LinkedIn"><i class="fab fa-linkedin fa-2x ml-2" style="color: rgb(67, 67, 255);"></i></a>
<!-- Add more social icons as needed -->
</div>
</div>
<!-- Footer -->
<div id="footer">
<p>© 2023 XYZ. All rights reserved.</p>
</div>
<!-- Font Awesome JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/js/all.min.js"></script>
<script>
function handleResponse(response) {
if (response.success) {
// If success, show an alert with the message
alert(response.message);
// Optionally, you can initiate the file download using JavaScript
// For example, redirect the browser to the download link
window.location.href = `/download/${response.fileName}.pdf`;
} else {
// If not success, show an alert with the error message
alert(response.message);
}
}
async function getUserInfo() {
const email = document.getElementById('email').value;
try {
const response = await fetch(`/getUserInfo?email=${encodeURIComponent(email)}`).then(response => response.json()).then(data => handleResponse(data)).catch(error => console.error('Error:', error));
const data = await response.json();
} catch (error) {
console.error('Error fetching user data:', error);
}
}
</script>
</body>
</html>
在您的 HTML 文件中,您尝试使用以下行启动下载
window.location.href = `/download/${response.fileName}.pdf`;
但是在 Express.js 应用程序中,您将路由定义为 /getUserInfo,而不是 /download。您需要确保 HTML 中的路由与服务器中定义的路由匹配。
此外,您还使用 .then() 和 async/await 来处理异步操作。为了保持 getUserInfo 函数的一致性,您可以只使用 async/await。
const response = await fetch(`/getUserInfo?email=${encodeURIComponent(email)}`);
const data = await response.json();
handleResponse(data);