C# 实现网页内容保存为图片并生成压缩包

目录

应用场景

实现代码

扩展功能(生成压缩包)

小结 


应用场景

我们在一个求职简历打印的项目功能里,需要根据一定的查询条件,得到结果并批量导出指定格式的文件。导出的格式可能有多种,比如WORD格式、EXCEL格式、PDF格式等,实现方式是通过设置对应的模板进行输出,实际情况是,简历的内容是灵活设置的,没有固定的格式,模板数量是不固定的。

通过动态页面技术,可以实现简历配置后的网页内容输出,但制作对应的各种模板会遇到开发效率和服务跟进的问题。为了保障原样输出,折中而简单的方案就是将动态输出的页面转化为图片格式。

实现代码

创建一个 UrlToImage 类,创建实例的时候传递指定的 URL, 并调用 SaveToImageFile(string outputFilename)方法,该方法传递要输出的文件名参数即可即可。

调用示例代码如下:

string url = "https://" + Request.Url.Host + "/printResume.aspx";
UrlToImage uti = new UrlToImage(url);
bool irv = uti.SaveToImageFile(Request.PhysicalApplicationPath + "\\test.jpg");
if(bool==false){
    Response.Write("save failed.");
    Response.End();
}

类及实现代码如下:

    public class UrlToImage
    {
        private  Bitmap m_Bitmap;
        private string m_Url;
        private string m_FileName = string.Empty;
        int initheight = 0;

        public UrlToImage(string url)
        {
            // Without file
            m_Url = url;
        }

        public UrlToImage(string url, string fileName)
        {
            // With file
            m_Url = url;
            m_FileName = fileName;
        }

        public Bitmap Generate()
        {
            // Thread
            var m_thread = new Thread(_Generate);
            m_thread.SetApartmentState(ApartmentState.STA);
            m_thread.Start();
            m_thread.Join();
            return m_Bitmap;
        }
        public bool SaveToImageFile(string filename)
        {
            Bitmap bt=Generate();
            if (bt == null)
            {
                return false;
            }
            bt.Save(filename);
            return File.Exists(filename);
        }
        private void _Generate()
        {
            var browser = new WebBrowser { ScrollBarsEnabled = false };
            browser.ScriptErrorsSuppressed = true;
            initheight = 0;
            browser.Navigate(m_Url);
            browser.DocumentCompleted += WebBrowser_DocumentCompleted;

            while (browser.ReadyState != WebBrowserReadyState.Complete)
            {
                Application.DoEvents();
            }

            browser.Dispose();
        }

        private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            // Capture
            var browser = (WebBrowser)sender;
            browser.ClientSize = new Size(browser.Document.Body.ScrollRectangle.Width, browser.Document.Body.ScrollRectangle.Bottom);
            browser.ScrollBarsEnabled = false;
            m_Bitmap = new Bitmap(browser.Document.Body.ScrollRectangle.Width, browser.Document.Body.ScrollRectangle.Bottom);
            browser.BringToFront();
            browser.DrawToBitmap(m_Bitmap, browser.Bounds);

            // Save as file?
            if (m_FileName.Length > 0)
            {
                // Save
                m_Bitmap.SaveJPG100(m_FileName);
            }
            if (initheight == browser.Document.Body.ScrollRectangle.Bottom)
            {
                browser.DocumentCompleted -= new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
            }
            initheight = browser.Document.Body.ScrollRectangle.Bottom;
        }
    }

生成压缩包

对于批量生成的图片文件,我们可以生成压缩包为客户提供下载功能,压缩功能引用的是ICSharpCode.SharpZipLib.dll,创建 ZipCompress 类的实例,ZipDirectory(zippath, zipfile, password) 方法,需要提供的参数包括,压缩的目录、生成的压缩文件名,压缩包的打开密码。

示例代码如下:

    string zippath = Request.PhysicalApplicationPath + "\\des\\" ;
    if (!Directory.Exists(zippath))
    {
        Directory.CreateDirectory(zippath);
    }
    string zipfile = Request.PhysicalApplicationPath + "\\des\\test.zip";
    ZipCompress allgzip = new ZipCompress();
    System.IO.DirectoryInfo alldi = new System.IO.DirectoryInfo(zippath);
    string password = "123456";

    allgzip.ZipDirectory(zippath, zipfile, password);
    //以下是生成完压缩包后,清除目录及文件
    string[] allfs = Directory.GetFiles(zippath);
    for (int i = 0; i < allfs.Length; i++)
    {
        File.Delete(allfs[i]);
    }
    Directory.Delete(zippath);  

类及实现代码如下:

 public class ZipCompress
    {
        
        public  byte[] Compress(byte[] inputBytes)
        {
            using (MemoryStream outStream = new MemoryStream())
            {
                using (GZipStream zipStream = new GZipStream(outStream, CompressionMode.Compress, true))
                {
                    zipStream.Write(inputBytes, 0, inputBytes.Length);
                    zipStream.Close(); //很重要,必须关闭,否则无法正确解压
                    return outStream.ToArray();
                }
            }
        }

        public  byte[] Decompress(byte[] inputBytes)
        {

            using (MemoryStream inputStream = new MemoryStream(inputBytes))
            {
                using (MemoryStream outStream = new MemoryStream())
                {
                    using (GZipStream zipStream = new GZipStream(inputStream, CompressionMode.Decompress))
                    {
                        zipStream.CopyTo(outStream);
                        zipStream.Close();
                        return outStream.ToArray();
                    }
                }

            }
        }
        public  string Compress(string input)
        {
            byte[] inputBytes = Encoding.Default.GetBytes(input);
            byte[] result = Compress(inputBytes);
            return Convert.ToBase64String(result);
        }
        public  string Decompress(string input)
        {
            byte[] inputBytes = Convert.FromBase64String(input);
            byte[] depressBytes = Decompress(inputBytes);
            return Encoding.Default.GetString(depressBytes);
        }
        public  void Compress(DirectoryInfo dir)
        {
            foreach (FileInfo fileToCompress in dir.GetFiles())
            {
                Compress(fileToCompress);
            }
        }
        public  void Decompress(DirectoryInfo dir)
        {
            foreach (FileInfo fileToCompress in dir.GetFiles())
            {
                Decompress(fileToCompress);
            }
        }
        public  void Compress(FileInfo fileToCompress)
        {
            using (FileStream originalFileStream = fileToCompress.OpenRead())
            {
                if ((File.GetAttributes(fileToCompress.FullName) & FileAttributes.Hidden) != FileAttributes.Hidden & fileToCompress.Extension != ".gz")
                {
                    using (FileStream compressedFileStream = File.Create(fileToCompress.FullName + ".gz"))
                    {
                        using (GZipStream compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress))
                        {
                            originalFileStream.CopyTo(compressionStream);
                        }
                    }
                }
            }
        }
        public  void Decompress(FileInfo fileToDecompress,string desfilename="")
        {
            using (FileStream originalFileStream = fileToDecompress.OpenRead())
            {
                string currentFileName = fileToDecompress.FullName;
                string newFileName = currentFileName.Remove(currentFileName.Length - fileToDecompress.Extension.Length);
                if (desfilename != "")
                {
                    newFileName = desfilename;
                }

                using (FileStream decompressedFileStream = File.Create(newFileName))
                {
                    using (GZipStream decompressionStream = new GZipStream(originalFileStream, CompressionMode.Decompress))
                    {
                        decompressionStream.CopyTo(decompressedFileStream);
                    }
                }
            }
        }


        
        public  void ZipDirectory(string folderToZip, string zipedFileName,string password)
        {
            ZipDirectory(folderToZip, zipedFileName,(password==""?string.Empty:password), true, string.Empty, string.Empty, true);
        }
 
 

        public  void ZipDirectory(string folderToZip, string zipedFileName, string password, bool isRecurse, string fileRegexFilter, string directoryRegexFilter, bool isCreateEmptyDirectories)
        {
             FastZip fastZip = new FastZip();
            fastZip.CreateEmptyDirectories = isCreateEmptyDirectories;
            fastZip.Password = password;
            fastZip.CreateZip(zipedFileName, folderToZip, isRecurse, fileRegexFilter, directoryRegexFilter);
       
        }
        public void UnZipDirectory(string zipedFileName, string targetDirectory, string password,string fileFilter=null)
        {
            FastZip fastZip = new FastZip();
            fastZip.Password = password;
            fastZip.ExtractZip(zipedFileName, targetDirectory,fileFilter);

        }


        public void UnZip(string zipFilePath, string unZipDir)
        {
           
            if (zipFilePath == string.Empty)
            {
                throw new Exception("压缩文件不能为空!");
            }
            if (!File.Exists(zipFilePath))
            {
                throw new FileNotFoundException("压缩文件不存在!");
            }
            //解压文件夹为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹  
            if (unZipDir == string.Empty)
                unZipDir = zipFilePath.Replace(Path.GetFileName(zipFilePath), Path.GetFileNameWithoutExtension(zipFilePath));
            if (!unZipDir.EndsWith("/"))
                unZipDir += "/";
            if (!Directory.Exists(unZipDir))
                Directory.CreateDirectory(unZipDir);
 
            using (var s = new ZipInputStream(File.OpenRead(zipFilePath)))
            {
 
                ZipEntry theEntry;
                while ((theEntry = s.GetNextEntry()) != null)
                {
                    string directoryName = Path.GetDirectoryName(theEntry.Name);
                    string fileName = Path.GetFileName(theEntry.Name);
                    if (!string.IsNullOrEmpty(directoryName))
                    {
                        Directory.CreateDirectory(unZipDir + directoryName);
                    }
                    if (directoryName != null && !directoryName.EndsWith("/"))
                    {
                    }
                    if (fileName != String.Empty)
                    {
                        using (FileStream streamWriter = File.Create(unZipDir + theEntry.Name))
                        {
 
                            int size;
                            byte[] data = new byte[2048];
                            while (true)
                            {
                                size = s.Read(data, 0, data.Length);
                                if (size > 0)
                                {
                                    streamWriter.Write(data, 0, size);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

    }

小结

对于生成的图片文件,我们还可以结合其它的API应用,来判断图片是否有被PS的情况,来提升和扩展应用程序的功能。另外,对于被访问的动态页面,建议使用访问控制,只有正常登录或提供访问令牌的用户才可以生成结果图片,以保证数据的安全性。

以上代码仅供参考,欢迎大家指正,再次感谢您的阅读!


相关文章

  • 五种多目标优化算法(MSSA、MOJS、NSWOA、MOPSO、MOAHA)性能对比(提供MATLAB代码)

    多目标优化算法是用于解决具有多个目标函数的优化问题的一类算法。其求解过程可以分为以下几个步骤:1. 定义问题:首先需要明确问题的目标函数和约束条件。多目标优化问题通常涉及多个目标函数,这些目标函数可能是相互矛盾的,因此需要进行权衡和平衡。2. 生成初始解集:通过某种方式生成初始解集,可以是随机生成、根据经验生成或者使用已有的解集。3. 评估解集:对初始解集中的每个解进行评估,计算其在各个目标函数上的值。评估方法可以根据具体问题选择,例如计算目标函数值、计算约束违反程度等。

  • 【C#小知识】c#中的delegate(委托)和event(事件)

    今天来介绍一下delegate和event。delegate在c#中可以定义一个函数类型,可以将函数作为一个对象来使用。event在c#中则可以看做一个函数的集合,event中包含了一个或多个函数。

  • C#使用重载方法实现不同类型数据的计算

    为了避免异常,可以先使用Decimal.Parse(string)方法将字符串转换为小数,然后再使用Convert.ToInt32(decimal)方法将小数转换为整数。如果一个类中存在两个以上的同名方法,并且方法的参数类型、个数或者顺序不同,当调用这样的方法时,编译器会根据传入的参数自动进行判断,决定调用哪个方法。例如,字符串是&quot;123.456&quot;,包含非数字字符&quot;.&quot;。重载方法就是方法名称相同,但是每个方法中参数的数据类型、个数或顺序不同的方法。如果字符串包含非数字字符,例如小数点,该方法将引发异常。

  • 在 Kubernetes 中查找 Pod IP 地址的 2 种方法

    如果只是需要快速查找某个Pod的IP地址或者进行简单的调试,使用kubectl命令可能更为方便。如果需要集成到自动化脚本、程序或工作流中,或者需要进行更复杂的查询和过滤操作,使用Kubernetes API是更好的选择。综合考虑使用场景和需求,可以根据具体情况选择合适的方法。通过本文我们详细介绍了在Kubernetes中查找Pod IP地址的两种方法:一种是通过kubectl命令,另一种是通过Kubernetes API查询。每种方法都有其优点和缺点,适用于不同的使用场景。

  • Makefile 和 Bash 脚本之间区别和联系

    在 Makefile 中可以调 Bash 脚本,或在 Makefile 中直接写入 Bash 命令。这使得在构建过程中执行更复杂的任务成为可能。Makefile和Bash脚本由于它们的设计目标和用途不同,它们在基本结构、命令执行、通配符使用、错误处理等方面存在显著的差异。了解这些差异对于正确编写和理解Makefile和Bash脚本至关重要。Makefile主要用于编译和构建软件项目,而Bash脚本则更广泛地应用于系统管理和自动化任务。在实际工作中,两者可以结合使用,以创建一个完整、自动化的构建和部署。

  • C#:Sleep() 和 Wait() 有什么区别

    Sleep() 和 Wait() 是两个不同的方法,用于控制线程的执行。

  • DockerUI如何部署结合内网穿透实现公网环境管理本地docker容器

    DockerUI是一个docker容器镜像的可视化图形化管理工具。DockerUI可以用来轻松构建、管理和维护docker环境。它是完全开源且免费的。基于容器安装方式,部署方便高效,浏览和维护docker单节点或集群节点worker和manager。DockerUI具有易于使用的界面。它不需要记住 docker 指令。只需下载镜像即可立即加入并完成部署。使用DockerUI并结合cpolar内网穿透可以更加轻松的管理docker和swarm,实现后台公网访问并管理,视觉性更加直观,后台开发更加便利。

  • C++ STL库详解:容器适配器stack和queue的结构及功能

    详细介绍了c++中的stack和queue两大容器适配器的功能、接口与使用方法,通过与vector、string等容器的配合使用来实现特定的功能。介绍了stack与queue的底层默认容器deque的原理及结构以及它的优缺陷。

  • C语言——深入理解指针(1)

    我们已经知道,32内存位计算器有32条地址总线,每条线有1和0两个状态,那么一个内存的编号就有32条地址线表示,一条地址线占一个比特位,那么32条地址线就是32个比特位,因此就是4个字节。,可以这么理解,32位的计算器有32条这样的线,每个线有两种状态,分别是0和1,那么32根线一共能表示2^32种状态,这样的每个状态就是我们的一个地址,他们分别储存在不同的硬件上,地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。当然,需要我们了解一下计算机的进制转换,

  • C,C++,C# 的区别

    C#是一种面向对象的编程语言,由微软开发。总的来说,C适合系统级编程和嵌入式开发,C++适合大型项目和需要高性能的应用程序开发,而C#适合Windows应用程序开发和.NET平台。C++是一种面向对象的编程语言,是C的扩展。C++也具有更强大的标准库,以支持更多的功能和任务。它具有简单的语法和较小的标准库,适合于高效的低级编程和处理底层细节。C++具有更高的性能和更好的底层控制能力,但开发过程中更复杂。C#的开发速度更快,代码更易于维护,但性能可能稍逊于C++。C,C++,C# 是三种不同的编程语言。

  • 一些著名的软件都用什么语言编写?

    比如你的两个朋友与你分别玩用VB、Java、与C++编写的“跑跑卡丁车”,你玩C++编写的游戏已经跑玩结束了,发现你的两个朋友还没开始跑呢,那是相当的卡啊。备注:曾经在智能手机的操作系统(Windows Mobile)考虑掺点C#写的程序,比如软键盘,结果因为写出来的程序太慢,实在无法和别的模块合并,最终又回到C++重写。:2008 年推出:C语言(有传言说是用Java开发的操作系统,但最近刚推出原生的C语言SDK): 部分JAVA(对外接口),主要为C++ (开源,可下载其源代码)

  • C# 十大排序算法

    这些排序算法具有不同的时间复杂度、空间复杂度和稳定性,适用于不同的排序场景。每种算法都有其独特的思想和实现方式,您可以根据具体的需求选择适合的排序算法。以上代码分别实现了10大算法。请注意,如果需要对其他类型的数据进行排序,需要进行相应的修改。

  • Java编程模型:VO,BO,PO,DO,DTO

    Java编程模型中的VO,BO,PO,DO,DTO提供了一种结构化和组织代码的方法。通过合理运用这些概念,可以使代码更具可读性、可维护性和可扩展性。在实际项目中,根据需求和架构设计,合理选择和运用这些概念将有助于构建清晰、高效的Java应用程序。