小程序开发实战:PDF转换为图片工具开发

目录

一、开发思路

1.1 申请微信小程序

1.2 编写后端接口

1.3 后端接口部署

1.4 微信小程序前端页面开发

1.5 运行效果

1.6 小程序部署上线


图片

今天给大家分享小程序开发系列,PDF转换为图片工具的开发实战,感兴趣的朋友可以一起来学习一下!

一、开发思路

  • 申请微信小程序

  • 编写后端接口

  • 后端接口部署

  • 微信小程序前端页面开发

  • 微信小程序部署上线

1.1 申请微信小程序

关于如何申请微信小程序这里就不过多介绍了,大家可以参考腾讯官方的文档,里面介绍的非常详细。

图片

1.2 编写后端接口

这里使用Java编程语言的SpringBoot框架来快速搭建WebAPI服务。因为涉及到PDF转换为图片,这里使用spire.pdf来实现。首先引入依赖项

      <dependency>
          <groupId>e-iceblue</groupId>
           <artifactId>spire.pdf.free</artifactId>
           <version>2.6.3</version>
           <scope>provided</scope>
       </dependency>

新建PdfUtils.java工具类库用来实现PDF转换为图片的功能

思路:通过微信小程序传递过来的文件转换为InputStream输出流,然后保存到服务器端,因为PDF可能涉及有多页,每一页单独为一个图片文件,然后调用图片拼接的方法实现所有页面图片合并为一张长图。注意:免费的spire.pdf支持10页之内的pdf转换,大家如果更高需求,可以考虑购买收费版。

主要代码如下:转换方法主函数

 
   /**
    * 根据文件流转换为图片
    *
    * @param stream
    * @return
    */
   public String pdftoimage(InputStream stream, String fileNameOld) {
   Date currentDate = new Date();
   SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS"); // 指定日期格式,包含毫秒

   String formattedDate = sdf.format(currentDate);
   String pathPath = "/mnt/files/" + formattedDate + "_" + fileNameOld;
   // 4、最终生成的doc所在的目录,默认是和引入的一个地方,开源时对外提供下载的接口。
   saveInputStreamToFile(stream, pathPath);

   String fileName = "result" + formattedDate + ".png";
   String desPath = "/mnt/files/" + fileName; // 构造文件名
   String sux = fileNameOld + "_" + formattedDate;// 临时文件前缀

   boolean result = false;
   try {
       // 0、判断输入的是否是pdf文件
       //第一步:判断输入的是否合法
       //boolean flag = isPDFFile(srcPath);
       //第二步:在输入的路径下新建文件夹
       boolean flag1 = create();

       if (flag1) {
           // 1、加载pdf
           PdfDocument pdf = new PdfDocument();
           //pdf.loadFromStream(stream);
           pdf.loadFromFile(pathPath);
           PdfPageCollection num = pdf.getPages();

           // 2、如果pdf的页数小于11,那么直接进行转化
           if (num.getCount() <= 10) {
               try {
                   for (int i = 0; i < pdf.getPages().getCount(); i++) {
                       BufferedImage image = pdf.saveAsImage(i, PdfImageType.Bitmap, 300, 300);
                       String imgTemp = imgPath + sux + (i + 1) + ".png"; // 构造输出文件路径
                       ImageIO.write(image, "PNG", new File(imgTemp));
                  }
                   pdf.close();
                   System.out.println("PDF转图片完成!");
                   MergeWordDocument.mergeImage(imgPath, desPath, sux);
                   clearFiles(imgPath, formattedDate);
                   clearFiles(pathPath, formattedDate);
              } catch (IOException e) {
                   e.printStackTrace();
                   System.out.println("PDF转图片失败: " + e.getMessage());
              }
          }
           // 3、否则输入的页数比较多,就开始进行切分再转化
           else {
               try {
                   for (int i = 0; i < 10; i++) {
                       BufferedImage image = pdf.saveAsImage(i, PdfImageType.Bitmap, 300, 300);
                       String imgTemp = imgPath + sux + (i + 1) + ".png"; // 构造输出文件路径
                       ImageIO.write(image, "PNG", new File(imgTemp));
                  }
                   pdf.close();
                   System.out.println("PDF转图片完成!");
                   MergeWordDocument.mergeImage(imgPath, desPath, sux);

              } catch (IOException e) {
                   e.printStackTrace();
                   System.out.println("PDF转图片失败: " + e.getMessage());
              } finally {
                   //clearFiles(imgPath);
                   clearFiles(pathPath, formattedDate);
              }
          }
      } else {
           System.out.println("输入的不是pdf文件");
           fileName = "";
           return fileName;
      }
  } catch (Exception e) {
       fileName = "";
       e.printStackTrace();
  } finally {
       //4、把刚刚缓存的split和doc删除
       if (result == true) {
           clearFiles(pathPath, formattedDate);
           clearFiles(splitPath, formattedDate);
           clearFiles(docPath, formattedDate);
      }
  }
   return fileName;
}

保存PDF文件到本地,然后使用后删除

/**
* 保存原始的pdf文件为了方便拆分
*
* @param inputStream
* @param filePath
*/
public static void saveInputStreamToFile(InputStream inputStream, String filePath) {

   // 使用try-with-resources自动关闭流
   try (FileOutputStream outputStream = new FileOutputStream(new File(filePath))) {
       byte[] buffer = new byte[1024];
       int length;

       // 读取输入流并写入到输出流
       while ((length = inputStream.read(buffer)) > 0) {
           outputStream.write(buffer, 0, length);
      }

       System.out.println("文件保存成功!");
  } catch (FileNotFoundException e) {
       e.printStackTrace();
  } catch (IOException e) {
       e.printStackTrace();
  }
}

多张图片合并逻辑

/**
* 多张图片合并之后的逻辑
* @param imagePath
* @param desPath
* @return
*/
public static boolean mergeImage(String imagePath, String desPath,String sux) {
   try {
       File folder = new File(imagePath);
       // 包含文件前缀的文件 简单解决并发的问题
       File[] imageFiles = folder.listFiles((dir, name) ->
              (name.toLowerCase().endsWith(".png") || name.toLowerCase().endsWith(".jpg") && name.contains(sux)));

       if (imageFiles != null && imageFiles.length > 0) {
           int maxWidth = 0;
           int totalHeight = 0;

           // 预先计算最大宽度和总高度
           for (File imageFile : imageFiles) {
               BufferedImage image = ImageIO.read(imageFile);
               maxWidth = Math.max(maxWidth, image.getWidth());
               totalHeight += image.getHeight();
               image.flush(); // 尝试释放资源
          }

           // 创建合并后的图片,仅初始化一次
           BufferedImage mergedImage = new BufferedImage(maxWidth, totalHeight, BufferedImage.TYPE_INT_ARGB);
           Graphics2D g2d = mergedImage.createGraphics();

           int currentY = 0;
           for (File imageFile : imageFiles) {
               BufferedImage image = ImageIO.read(imageFile);
               g2d.drawImage(image, 0, currentY, null);
               currentY += image.getHeight();
               image.flush(); // 处理完后释放当前图片资源
          }

           g2d.dispose();

           // 保存合并后的图片
           ImageIO.write(mergedImage, "PNG", new File(desPath));
           System.out.println("图片合并完成!");
           for (File file : imageFiles){
               if (file.exists()) {
                   if (file.delete()) {
                       System.out.println("文件 " + file.getName() + " 已被删除");
                  } else {
                       System.out.println("无法删除文件 " + file.getName());
                  }
              } else {
                   System.out.println("文件 " + file.getName() + " 不存在");
              }
          }

      } else {
           System.out.println("输入文件夹中没有图片文件!");
      }
  } catch (IOException e) {
       e.printStackTrace();
       System.out.println("图片合并失败: " + e.getMessage());
  }
   return true;
}

新建控制器PdfApi.java

用来接收小程序调用传递过来的参数,需要判断传递过来的文件是否为图片格式,然后调用转换方法即可。

 /**
    * pdf转图片 多页转一张图
    * @param uploadFile
    * @return
    * @throws IOException
    */
   @PostMapping("pdfconvertimage")
   public String upload(@RequestPart("file") MultipartFile uploadFile) throws IOException {
       if (null == uploadFile) {
           return null;
      }
       // BMP、JPG、JPEG、PNG、GIF
       String fileName = uploadFile.getOriginalFilename().toLowerCase();
       if (!fileName.endsWith(".pdf")) {
           return null;
      }
       //String image= PdfUtils.pdf(uploadFile.getInputStream(),Integer.valueOf(type));
       String image= PdfUtils.pdfToPng(uploadFile.getInputStream(),fileName);
       // 返回响应实体
       return image;
  }
 

1.3 后端接口部署

因为微信小程序调用第三方接口需要https域名形式,所以接口开发完成后,需要部署到云服务器,然后申请域名、申请SSL证书,确保接口可以通过https域名正常访问。并且在微信小程序开发设置配置request合法域名白名单,保证接口可以调通。

图片

1.4 微信小程序前端页面开发

打开微信开发者工具,然后微信小程序管理员扫码登录自己的微信小程序。这里主要给大家贴出主要的代码以及实现思路。具体界面如下:

上传方式:支持微信会话文件上传、直接输入PDF文件的URL,转换成功后可以点击下载按钮进行下载图片。

图片

wxml文件代码如下:


<view style="text-align: center;">
<image style="width: 98%;"  src="推广图片"></image>
</view>
<view class="selectSection">
    <text class="textmag">上传方式:</text>
    <radio-group bindchange="radioChange" class="radio-group">
        <label class="radio" wx:for="{{direction}}" wx:key="i">
            <icon class="radioIcon {{item.checked?'actIcon':''}}"></icon>
            <radio checked="{{item.checked}}" value="{{item.name}}"></radio>{{item.value}}
        </label>
    </radio-group>
</view>
<view class="container">
    <view wx:if="{{directionType==1}}" class="item"> <button style="width: 120px;" class="butss" bindtap="chooseFile">上传pdf文件</button></view>
    <view wx:if="{{directionType==2}}" class="item"> <button style="width: 120px;" class="butss" bindtap="chooseFileNew">生成图片</button></view>
    <view class="item"> <button style="width: 90px;" class="butss" bindtap="saveTap">下载</button></view>
    <view class="item"> <button style="width: 90px;" class="butss" bindtap="clearTap">清空</button></view>
</view>

<view style="padding: 20px;">
    <span style="color: red;font-size: 12px;">温馨提示:目前支持10页以内的pdf文件转换</span>
</view>
<view>
  <textarea  auto-height bindinput="handleInput" class="input-content" value="{{uploadUrl}}"  placeholder="请输入pdf文件url" wx:if="{{directionType==2}}"></textarea>
</view><view class="instruction"> 
    <span style="color: black;padding-left: 10px;">结果文件:{{data}}</span>
</view>

js主要代码:

 // 选择微信会话文件 然后直接调用上传接口
    chooseFile: function () {
        var that = this;
        wx.showLoading({
            title: '图片上传处理中,请稍后...',
        });
        wx.chooseMessageFile({
            count: 1,
            type: 'file',
            extension: ['pdf'], // 限定选择的文件格式为.doc, .docx, .pdf
            success: function (res) {
                const tempFilePath = res.tempFiles[0].path;
                if (res.tempFiles[0].size > 10 * 1024 * 1024) { // 限定文件大小为2MB
                    wx.showToast({
                        title: '文件大小超过限制,请选择小于10MB的文件',
                        icon: 'none'
                    });
                    return;
                }
                that.setData({
                    pdfPath: tempFilePath                })wx.uploadFile({
 url: '后端接口API',
 filePath: tempFilePath,
 formData: { 
 },
 name: 'file',
 success: function (res) {
 if (res.statusCode == "200") { 
 that.setData({
 imageUrl: res.data,// 直接可以访问的url
 data: res.data
 }); 
 wx.showToast({
 title: '转换成功',
 icon: 'success',
 duration: 2000
 });
 } else {
 wx.showToast({
 title: '转换失败,请联系管理员',
 icon: 'none',
 duration: 2000
 });
 }
 },
 fail: function (res) {
 wx.showToast({
 title: '上传失败',
 icon: 'none',
 duration: 2000
 });
 }
 });
 },
 fail: function (res) {
 console.error('选择文件失败', res);
 wx.showToast({
 title: '选择文件失败',
 icon: 'none',
 duration: 2000
 });
 }
 });
 },
 
 // 下载按钮事件
 saveTap: function () {
 if (this.data.imageUrl) {
 wx.downloadFile({
 url: this.data.imageUrl,
 success: function (res) {
 if (res.statusCode === 200) {
 var filePath = res.tempFilePath;
 // 调用保存图片方法
 wx.saveImageToPhotosAlbum({
 filePath: filePath,
 success: function (res) {
 wx.showToast({
 title: '保存成功',
 icon: 'success',
 duration: 2000
 });
 },
 fail: function (err) {
 console.error(err);
 wx.showToast({
 title: '保存失败',
 icon: 'none',
 duration: 2000
 });
 }
 });
 }
 },
 fail: function (err) {
 console.error(err);
 wx.showToast({
 title: '下载失败',
 icon: 'none',
 duration: 2000
 });
 }
 });
 } else {
 wx.showToast({
 title: '请先上传pdf文件,转换成功后再保存',
 icon: 'none',
 duration: 2000
 });
 }
 },


1.5 运行效果

选择pdf文件上传

图片

转换成功之后的结果文件如下:

图片

然后可以点击下载按钮下载图片文件。整体转还原度还是很高的。

1.6 小程序部署上线

该步骤对于小程序开发的朋友来说,还是非常简单的,这里就不过多介绍了,大家有问题的话,欢迎沟通交流!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/902149.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux中级(NFS服务器)

NFS&#xff1a;用于在NNIX/Linux主机之间进行文件共享的协议 流程&#xff1a;首先服务端开启RPC服务&#xff0c;并开启111端口&#xff0c;服务器端启动NFS服务&#xff0c;并向RPC注册端口信息&#xff0c;客户端启动RPC&#xff0c;向服务器RPC服务请求NFS端口&#xff0…

anaconda 创建环境失败 解决指南

anaconda 创建环境失败 解决指南 一、问题描述 我在宿舍有一台电脑。由于我经常泡在实验室&#xff0c;所以那台电脑不是经常用&#xff0c;基本吃灰。昨天晚上突然有在那台电脑上使用Camel-AI部署多智能体协同需求&#xff0c;便戳开了电脑&#xff0c;问题也随之而来。 当…

汽车级DC-DC转换器英飞凌TLF35584

上汽荣威都在用的汽车级DC-DC转换器英飞凌TLF35584 今天平台君从IPBrain数据库中给大家带来的一款由Infineon(英飞凌)推出的一款多路输出安全电源芯片,具备高可靠性和安全性。适用于汽车电子系统中的多种应用场景,如车身控制、安全气囊、防抱死制动系统,电子稳定控制系统等。…

设计模式基础知识以及典型设计模式总结(上)

1. 基础 1. 什么是设计模式&#xff1f; 设计模式是指在软件开发中&#xff0c;经过验证的&#xff0c;用于解决在特定环境下重复出现的特定问题的解决方案。 简单的说&#xff0c;设计模式是解决问题的固定套路。 慎用设计模式。 2. 设计模式是怎么来的&#xff1f; 满足…

Unity3D学习FPS游戏(4)重力模拟和角色跳跃

前言&#xff1a;前面两篇文章&#xff0c;已经实现了角色的移动和视角转动&#xff0c;但是角色并没有办法跳跃&#xff0c;有时候还会随着视角移动跑到天上。这是因为缺少重力系统&#xff0c;本篇将实现重力和角色跳跃功能。觉得有帮助的话可以点赞收藏支持一下&#xff01;…

fmql之Linux RTC

模拟i2c&#xff0c;连接rtc芯片。 dts&#xff1a; /{ // 根节点i2c_gpio: i2c-gpio {#address-cells <1>;#size-cells <0>;compatible "i2c-gpio";// MIO56-SDA, MIO55-SCL // 引脚编号gpios <&portc 2 0&portc 1 0 >;i2c-gp…

Unity3D学习FPS游戏(2)简单场景、玩家移动控制

前言&#xff1a;上一篇的时候&#xff0c;我们已经导入了官方fps的素材&#xff0c;并且对三维模型有了一定了解。接下来我们要构建一个简单的场景让玩家能够有地方移动&#xff0c;然后写一个简单的玩家移动控制。 简单场景和玩家移动 简单场景玩家移动控制玩家模型视野-摄像…

单细胞配色效果模拟器 | 简陋版(已有颜色数组)

目的&#xff1a;假设你有一组颜色了&#xff0c;怎么模拟查看它们在单细胞DimPlot中的美学效果呢&#xff1f;要足够快&#xff0c;还要尽可能有模拟效果。 1. 尝试1: 随机矩阵&#xff0c;真的UMAP降维后绘图&#xff08;失败&#xff09; 造一个随机矩阵&#xff0c;使用S…

华为鸿蒙HarmonyOS应用开发者高级认证视频及题库答案

华为鸿蒙开发者高级认证的学习资料 1、课程内容涵盖HarmonyOS系统介绍、DevEco Studio工具使用、UI设计与开发、Ability设计与开发、分布式特性、原子化服务卡片以及应用发布等。每个实验都与课程相匹配&#xff0c;帮助加深理解并掌握技能 2、学习视频资料 华为HarmonyOS开发…

全能大模型GPT-4o体验和接入教程

GPT-4o体验和接入教程 前言一、原生API二、Python LangchainSpring AI总结 前言 Open AI发布了产品GPT-4o&#xff0c;o表示"omni"&#xff0c;全能的意思。 GPT-4o可以实时对音频、视觉和文本进行推理&#xff0c;响应时间平均为 320 毫秒&#xff0c;和人类之间对…

动态规划 —— 斐波那契数列模型-解码方法

1. 解码方法 题目链接&#xff1a; 91. 解码方法 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/decode-ways/description/ 2. 题目解析 1. 对字母A - Z进行编码1-26 2. 11106可以解码为1-1-10-6或者11-10-6, 但是11-1-06不能解码 3. 0n不能解码 4. …

微知-Lecroy力科的PCIe协议分析仪型号命名规则(PCIe代,金手指lanes数量)

文章目录 要点主要型号命名规则各代主要产品图片Summit M616 协议分析仪/训练器Summit T516 分析仪Summit T416 分析仪Summit T3-16分析仪Summit T28 分析仪 综述 要点 LeCroy(力科)成立于1964年&#xff0c;是一家专业生产示波器厂家。在美国纽约。一直把重点放在研制改善生产…

【Linux】线程池详解及其基本架构与单例模式实现

目录 1.关于线程池的基本理论 1.1.线程池是什么&#xff1f; 1.2.线程池的应用场景&#xff1a; 2.线程池的基本架构 2.1.线程容器 2.2.任务队列 2.3.线程函数&#xff08;HandlerTask&#xff09; 2.4.线程唤醒机制 3.添加单例模式 3.1.单例模式是什么&…

【Canvas与图标】六色彩虹圆角六边形图标

【成图】 120*120的png图标 以下是各种大小图&#xff1a; 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>六色彩虹圆角六边形…

总裁主题CeoMax-Pro主题7.6开心版

激活方式&#xff1a; 1.授权接口源码ceotheme-auth-api.zip搭建一个站点&#xff0c;绑定www.ceotheme.com域名&#xff0c;并配置任意一个域名的 SSL 证书。 2.在 hosts 中添加&#xff1a;127.0.0.1 www.ceotheme.com 3.上传class-wp-http.php到wp-includes目录&#xff…

Java案例——屏蔽信息

首先这次的案例需要用到substring方法&#xff0c;先了解一下&#xff1a; 首先我们来加密一下电话号码&#xff1b; package String; public class Demo_06 {public static void main(String[] args) {// 定义一个电话号码字符串String phoneNumber"13111112598"…

亚信安全DeepSecurity中标知名寿险机构云主机安全项目

近日&#xff0c;亚信安全DeepSecurity成功中标国内知名寿险机构的云主机安全项目。亚信安全凭借在云主机安全防护领域的突出技术优势&#xff0c;结合安全运营的能力&#xff0c;以“实战化”为指导&#xff0c;为用户提供无惧威胁攻击、无忧安全运营的一站式云安全体系&#…

unity中GameObject介绍

在 Unity 中&#xff0c;Cube和Sphere等基本几何体是 Unity 引擎的内置预制体&#xff08;Prefabs&#xff09;&#xff0c;它们属于 Unity 中的GameObject 系统&#xff0c;可以在 Unity 的 Hierarchy 视图或 Scene 视图中右键点击&#xff0c;然后在弹出的菜单中选择 3D Obje…

模型训练识别手写数字(二)

模型训练识别手写数字&#xff08;一&#xff09;使用手写数字图像进行模型测试 一、生成手写数字图像 1. 导入所需库 import cv2 import numpy as np import oscv2用于计算机视觉操作。 numpy用于处理数组和图像数据。 os用于文件和目录操作。 2. 初始化画布 canvas np.z…

多线程——线程的状态

线程状态的意义 ‌线程状态的意义在于描述线程在执行过程中的不同阶段和条件&#xff0c;帮助开发者更好地管理和调度线程资源。 线程的多种状态 线程的状态是一个枚举类型&#xff08;Thread.State&#xff09;&#xff0c;可以通过线程名.getState&#xff08;&#xff09…