赞
踩
最近有这么一个需求,PDF太大了,需要拆分成多份,要求每份的PDF不大于10MB。刚开始,我认为这应该很简单,但是足足耗费了一天的时间去解决该问题。
首先是核心部分当然是PDF如何进行分割了,不仅仅要分割,还要统计分割的大小。然后我还不想伤硬盘,直接写在硬盘上判断文件是否超过了大小,然后回溯。所以我只能在运行的时候检测文件大小。核心部分的难度基本如上所述。下面来讲解一下我是如何解决这个问题的吧。还有一个最难的问题,那就是如何不用UnityEditor库来打开FilePanel
又叫FileDialog
。
首先在Visual Studio Community中安装.NET 桌面开发,用来下载需要的dll。当然也可以直接在Unity中打开VS后下载,我这其实是脱裤子放屁,但是我想保持Unity的干净,不想事后卸载删除,所以我采用了这个方法。
安装完成后创建一个控制台应用。
将框架选为.NET Framework 2.0。
打开后,在 工具》NuGet包管理器》管理解决方案的NuGet程序包。在浏览中搜索iTextSharp
并安装。安装完成后,在.sln
文件的同路径下有一个packages包,把里面BouncyCastle.1.8.9
和iTextSharp.5.5.13.3
的lib文件夹下的.dll
文件拷贝到Unity项目的Plugins(自己创建)文件夹中。
明确已知,没有提供方法来确定生成后的大小。但是我们可以先在MemoryStream
中存储一下,并获取MemoryStream
空间所消耗的大小。
下面给出示例代码。
long GetMemorySize(string inputFilePath)
{
PdfReader reader = new PdfReader(inputFilePath);
Document document = new Document();
MemoryStream memoryStream = new MemoryStream();
PdfCopy copy = new PdfCopy(document, memoryStream);
document.Open();
copy.AddPage(copy.GetImportedPage(reader, 1));//这里页码是从1到最后的,不是常规的从0开始。
long memorySize = memoryStream.Length;
reader.Close();
document.Close();
memoryStream.Close();
return memorySize;
}
这份脚本为PDFSplitter
。将来挂在GameController
空物体上。
using UnityEngine; using System.IO; using iTextSharp.text; using iTextSharp.text.pdf; using UnityEngine.UI; using System.Collections.Concurrent; using System.Collections; public class PDFSplitter : MonoBehaviour { private string inputFilePath; private string outputDirectory; private long maxFileSizeMB = 10 * 1024 * 1024; private PdfReader reader; public InputField from; public InputField save; public Slider slider; private ConcurrentQueue<float> resultQueue = new ConcurrentQueue<float>(); public void Splitter() { System.Threading.Thread thread = new System.Threading.Thread(Run); thread.Start(); StartCoroutine(updateSlider()); } IEnumerator updateSlider() { float res; while (true) { while (!resultQueue.TryDequeue(out res)) ; if (res < 0) { slider.value = 1.0f; GetComponent<GameController>().showFinishPanel = true; break; } slider.value = res; yield return null; } } public void Run() { int cnt = 1; int pre = 1; float rate; inputFilePath = from.text; outputDirectory = save.text; reader = new PdfReader(inputFilePath); PdfReader.unethicalreading = true; Document document = null; MemoryStream memoryStream = null; PdfCopy copy = null; int pageCount = reader.NumberOfPages; for (int i = 1; i <= pageCount; i++) { if (document == null) { document = new Document(); memoryStream = new MemoryStream(); copy = new PdfCopy(document, memoryStream); document.Open(); copy.AddPage(copy.GetImportedPage(reader, i)); } else { copy.AddPage(copy.GetImportedPage(reader, i)); if (memoryStream.Length > maxFileSizeMB) { PDF_Writer(pre, i - 1, cnt++.ToString("00")); document.Close(); memoryStream.Close(); document = null; memoryStream = null; copy = null; pre = i--; } } rate = 1.0f * i / pageCount; resultQueue.Enqueue(rate); } document.Close(); PDF_Writer(pre, pageCount, cnt.ToString("00")); reader.Close(); resultQueue.Enqueue(-1.0f); } void PDF_Writer(int start, int end, string name) { Document document = new Document(); PdfCopy copy = new PdfCopy(document, new FileStream(outputDirectory + $"//{name}.pdf", FileMode.Create)); document.Open(); for (int i = start; i <= end; i++) copy.AddPage(copy.GetImportedPage(reader, i)); document.Close(); } }
首先在Github链接中下载StandaloneFileBrowser。
之后,就将下载的Packages直接拖进项目中导入。
用这个包之后为了能够打包成功我们需要将Unity的.Net框架改为 .Net 4.x
我们这里只需要使用StandaloneFileBrowser.OpenFilePanel
和StandaloneFileBrowser.OpenFolderPanel
可能导入时候会出一些莫名其妙的错误,不用在意,只要能编译正常用,打包正常打出去,就可以。奇怪的是打包成功之后这些错误莫名其妙的消失掉了。
这份脚本为PathBar
。将来挂在涉及路径选择的自定义物体上。
using UnityEngine; using UnityEngine.UI; using SFB; public class PathBar : MonoBehaviour { [HideInInspector] public string path; public InputField info; public void SelectFilePath() { var extensions = new[] { new ExtensionFilter("PDF Files", "pdf"), new ExtensionFilter("All Files", "*") }; path = StandaloneFileBrowser.OpenFilePanel("Open PDF File", "", extensions, false)[0]; info.text = path; } public void SelectFolderPath() { path = StandaloneFileBrowser.OpenFolderPanel("Select save Folder", "", false)[0]; info.text = path; } }
这份脚本为GameController
。将来挂在GameController
空物体上。
using System.Collections; using UnityEngine; using UnityEngine.UI; using System.IO; public class GameController : MonoBehaviour { public GameObject FinishPanel; public GameObject WarningPanel; [HideInInspector] public bool showFinishPanel; public InputField from; public InputField save; public void Run() { if (!File.Exists(from.text) || !Directory.Exists(save.text) || Path.GetExtension(from.text).ToLower() != ".pdf") { WarningPanel.GetComponent<PanelController>().Open(); return; } showFinishPanel = false; GetComponent<PDFSplitter>().Splitter(); StartCoroutine(ShowFinishPanel()); } IEnumerator ShowFinishPanel() { while (!showFinishPanel) yield return null; FinishPanel.GetComponent<PanelController>().Open(); } }
这份脚本为PanelController
。将来挂在一些用来提示的物体上。
using UnityEngine;
public class PanelController : MonoBehaviour
{
public void Close()
{
gameObject.SetActive(false);
}
public void Open()
{
gameObject.SetActive(true);
}
}
界面如下,很好拆分上面的我称之为PathBar,用来选择路径。
中间是Button,用来执行分割操作。
最底下是进度条,可以参考B站视频 BV1iW411D78W
大概在55分钟左右会有讲解。我这里多做了一步操作,把slider的图片全部设置成了None。
下面看一下我的目录结构
下面看一下我的目录结构
PDFSplitter 资源包下载链接【CSDN】免费
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。