Vue 发送 PDF 文件链接到 WinForm 程序进行打印

Vue 发送 PDF 文件链接到 WinForm 程序进行打印的完整流程如下:

1. Vue 端

Vue 通过 fetchaxios 发送 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. 测试流程

  1. 运行 WinForms 程序 ,它会监听 http://localhost:5000/print
  2. Vue 页面点击按钮,发送 PDF URL 进行打印。
  3. 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();
}

解释

  1. CORS 预检请求 (OPTIONS) 处理

    • OPTIONS 请求是浏览器在 跨域请求 时自动发送的,用于检查服务器是否允许这个请求。
    • Access-Control-Allow-Origin: * 允许来自任何域的请求。
    • Access-Control-Allow-Methods: POST, GET, OPTIONS 允许前端发送 POST 请求。
    • Access-Control-Allow-Headers: Content-Type 允许发送 JSON 数据。
  2. 添加 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" }
  }
);

最终效果

  1. WinForms API 允许跨域请求 ,不会再被 OPTIONS 请求阻挡。
  2. Vue 可以正常发送 POST 请求,WinForms API 正常接收并打印 PDF 🎉。

这样,Vue 和 WinForm 端的通信就能顺利进行了!🚀

相关推荐
mldong4 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排5 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60615 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角6 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!7 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰7 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi8 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen9 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马10 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒10 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端