Vue 发送 PDF 文件链接到 WinForm 程序进行打印的完整流程如下:
1. Vue 端
Vue 通过 fetch
或 axios
发送 PDF 文件的 URL 给 WinForms 程序(WinForms 需要开启一个本地 API)。
vue
<template>
<div>
<button @click="sendPrintRequest">打印PDF</button>
</div>
</template>
<script>
import axios from "axios";
export default {
methods: {
sendPrintRequest() {
const pdfUrl = "http://yourserver.com/sample.pdf"; // 你的 PDF 地址
axios
.post("http://localhost:5000/print", { pdfUrl }) // 本地 WinForms 监听的 API
.then((response) => {
console.log("打印请求成功", response.data);
})
.catch((error) => {
console.error("打印请求失败", error);
});
},
},
};
</script>
2. WinForm 后端
WinForms 需要开启一个本地 HTTP 服务器,用于接收 Vue 发送的 PDF 链接,并调用本地打印机打印。
1) 创建一个 Web API
使用 HttpListener
监听 http://localhost:5000/print
,并接收 Vue 发送的 JSON 数据。
csharp
using System;
using System.Drawing.Printing;
using System.IO;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Diagnostics;
class Program
{
static void Main()
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://localhost:5000/"); // 监听本地端口
listener.Start();
Console.WriteLine("打印服务器已启动...");
while (true)
{
HttpListenerContext context = listener.GetContext();
Task.Run(() => HandleRequest(context));
}
}
static async void HandleRequest(HttpListenerContext context)
{
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
if (request.HttpMethod == "POST" && request.Url.AbsolutePath == "/print")
{
using (StreamReader reader = new StreamReader(request.InputStream, request.ContentEncoding))
{
string requestBody = await reader.ReadToEndAsync();
var data = JsonSerializer.Deserialize<PrintRequest>(requestBody);
if (data != null && !string.IsNullOrEmpty(data.PdfUrl))
{
Console.WriteLine("收到打印请求:" + data.PdfUrl);
bool success = PrintPdf(data.PdfUrl);
response.StatusCode = success ? 200 : 500;
string responseText = success ? "打印成功" : "打印失败";
byte[] buffer = Encoding.UTF8.GetBytes(responseText);
response.OutputStream.Write(buffer, 0, buffer.Length);
}
}
}
response.Close();
}
static bool PrintPdf(string pdfUrl)
{
try
{
string localPath = Path.Combine(Path.GetTempPath(), "temp.pdf");
using (WebClient client = new WebClient())
{
client.DownloadFile(pdfUrl, localPath);
}
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = localPath,
Verb = "print",
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
Process.Start(psi);
return true;
}
catch (Exception ex)
{
Console.WriteLine("打印出错:" + ex.Message);
return false;
}
}
class PrintRequest
{
public string PdfUrl { get; set; }
}
}
2) Aspose.Pdf 插件打印
复杂一点的,使用Aspose.Pdf打印插件进行答应
csharp
using Aspose.Pdf;
using PdfiumViewer;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing.Printing;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;
namespace ConsoleApp1
{
/// <summary>
/// VUE发送PDF文件到客户端 客户端调用默认打印机 开始打印
/// </summary>
public class VueToPrintHelper
{
public VueToPrintHelper()
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://localhost:5066/");
listener.Start();
Console.WriteLine("Listening for requests...");
// 获取所有已安装的打印机
foreach (string printer in PrinterSettings.InstalledPrinters)
{
Console.WriteLine(printer);
}
while (true)
{
HttpListenerContext context = listener.GetContext();
Task.Run(() => HandleRequest(context));
}
}
static async void HandleRequest(HttpListenerContext context)
{
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
// 设置 CORS 头部,允许前端访问
response.AddHeader("Access-Control-Allow-Origin", "*");
response.AddHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
response.AddHeader("Access-Control-Allow-Headers", "Content-Type");
// 处理 OPTIONS 预检请求 (CORS 预检)
if (request.HttpMethod == "OPTIONS")
{
response.StatusCode = 200;
response.Close();
return;
}
if (request.HttpMethod == "POST" && request.Url.AbsolutePath == "/print")
{
using (StreamReader reader = new StreamReader(request.InputStream, request.ContentEncoding))
{
string requestBody = await reader.ReadToEndAsync();
var data = JsonSerializer.Deserialize<PrintRequest>(requestBody);
if (data != null && !string.IsNullOrEmpty(data.PdfUrl))
{
Console.WriteLine("收到打印请求:" + data.PdfUrl);
bool success = PrintPdf(data.PdfUrl);
response.StatusCode = success ? 200 : 500;
string responseText = success ? "打印成功" : "打印失败";
byte[] buffer = Encoding.UTF8.GetBytes(responseText);
response.OutputStream.Write(buffer, 0, buffer.Length);
}
}
}
response.Close();
}
static bool PrintPdf(string pdfUrl)
{
try
{
var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "print_pdfs");
if (!Directory.Exists(filePath))
{
Directory.CreateDirectory(filePath);
}
string localPath = Path.Combine(filePath, Path.GetFileName(pdfUrl));
if (!File.Exists(localPath))
{
using (WebClient client = new WebClient())
{
client.DownloadFile(pdfUrl, localPath);
}
}
PrintPdf3(localPath);
Console.WriteLine("打印命令已发送");
return true;
}
catch (Exception ex)
{
Console.WriteLine("打印出错:" + ex.Message);
return false;
}
}
static string DownloadPdf(string pdfUrl)
{
string tempFilePath = Path.Combine(Path.GetTempPath(), "document.pdf");
using (WebClient client = new WebClient())
{
try
{
client.DownloadFile(pdfUrl, tempFilePath);
return tempFilePath;
}
catch (Exception ex)
{
Console.WriteLine("Error downloading PDF: " + ex.Message);
return null;
}
}
}
static void PrintPDF(string filePath)
{
using (var document = PdfDocument.Load(filePath)) // 使用 PdfiumViewer 加载 PDF
{
PrintDocument printDocument = document.CreatePrintDocument();
printDocument.Print();
}
}
static void PrintPDF2(string filePath)
{
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = filePath, // 这里直接是 PDF 文件
Verb = "print", // 使用默认应用程序打印
UseShellExecute = true, // 关键:让 Windows 自动用默认应用打开
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
Process.Start(psi);
}
static void PrintPdf3(string pdfPath)
{
// 加载 PDF 文档
Document pdfDocument = new Document(pdfPath);
// 创建 PdfViewer 实例
using (Aspose.Pdf.Facades.PdfViewer pdfViewer = new Aspose.Pdf.Facades.PdfViewer(pdfDocument))
{
pdfViewer.AutoResize = true; // 自动调整大小
pdfViewer.AutoRotate = true; // 自动旋转
pdfViewer.PrintPageDialog = false; // 不弹出打印对话框
// 设置打印机
Aspose.Pdf.Printing.PrinterSettings printerSettings = new Aspose.Pdf.Printing.PrinterSettings();
printerSettings.PrinterName = PrinterSettings.InstalledPrinters[0]; // 选择默认打印机
// 创建 PageSettings 并绑定 PrinterSettings
Aspose.Pdf.Printing.PageSettings pageSettings = new Aspose.Pdf.Printing.PageSettings();
pageSettings.PrinterSettings = printerSettings;
// 进行打印
pdfViewer.PrintDocumentWithSettings(pageSettings, printerSettings);
}
Console.WriteLine("✅ PDF 已发送到打印机!");
}
}
class PrintRequest
{
public string PdfUrl { get; set; }
}
}
3. 测试流程
- 运行 WinForms 程序 ,它会监听
http://localhost:5000/print
。 - Vue 页面点击按钮,发送 PDF URL 进行打印。
- WinForms 下载 PDF 并调用本地打印机 进行打印。
这种方式适用于局域网环境,Vue 只需要调用本地的 WinForms 服务器即可,不涉及复杂的文件传输流程。🎯
请求变成 OPTIONS
,通常是由于 CORS 预检请求 (Preflight Request)。当你从 Vue (浏览器) 发送 POST
请求到 http://localhost:5000/print
,而此 API 位于 不同的端口 ,浏览器会先发送一个 OPTIONS
请求来检查服务器是否允许跨域访问。
解决方案
你需要在 WinForms API (C#) 中处理 OPTIONS
请求,返回适当的 CORS 头部,让浏览器允许跨域请求。
修改 C# 代码
在 HandleRequest
方法中添加对 OPTIONS
请求的处理:
csharp
static async void HandleRequest(HttpListenerContext context)
{
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
// 处理 OPTIONS 预检请求 (CORS 预检)
if (request.HttpMethod == "OPTIONS")
{
response.AddHeader("Access-Control-Allow-Origin", "*");
response.AddHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
response.AddHeader("Access-Control-Allow-Headers", "Content-Type");
response.StatusCode = 200;
response.Close();
return;
}
if (request.HttpMethod == "POST" && request.Url.AbsolutePath == "/print")
{
using (StreamReader reader = new StreamReader(request.InputStream, request.ContentEncoding))
{
string requestBody = await reader.ReadToEndAsync();
var data = JsonSerializer.Deserialize<PrintRequest>(requestBody);
if (data != null && !string.IsNullOrEmpty(data.PdfUrl))
{
Console.WriteLine("收到打印请求:" + data.PdfUrl);
bool success = PrintPdf(data.PdfUrl);
response.StatusCode = success ? 200 : 500;
string responseText = success ? "打印成功" : "打印失败";
byte[] buffer = Encoding.UTF8.GetBytes(responseText);
response.OutputStream.Write(buffer, 0, buffer.Length);
}
}
}
// 添加 CORS 头部 (允许跨域请求)
response.AddHeader("Access-Control-Allow-Origin", "*");
response.Close();
}
解释
-
CORS 预检请求 (
OPTIONS
) 处理OPTIONS
请求是浏览器在 跨域请求 时自动发送的,用于检查服务器是否允许这个请求。Access-Control-Allow-Origin: *
允许来自任何域的请求。Access-Control-Allow-Methods: POST, GET, OPTIONS
允许前端发送POST
请求。Access-Control-Allow-Headers: Content-Type
允许发送 JSON 数据。
-
添加 CORS 头部
- 在
POST
处理完成后,仍然需要 返回Access-Control-Allow-Origin: *
,否则浏览器可能仍然拒绝请求。
- 在
Vue 端 (可选优化)
你也可以在 Vue 端的 axios
请求中 显式添加 Content-Type
,避免某些情况下触发 OPTIONS
预检请求:
javascript
axios.post("http://localhost:5000/print",
{ pdfUrl: "http://yourserver.com/sample.pdf" },
{
headers: { "Content-Type": "application/json" }
}
);
最终效果
- WinForms API 允许跨域请求 ,不会再被
OPTIONS
请求阻挡。 - Vue 可以正常发送
POST
请求,WinForms API 正常接收并打印 PDF 🎉。
这样,Vue 和 WinForm 端的通信就能顺利进行了!🚀