工作上有愈來愈多需要加密壓縮的場合,過去我慣用的做法是從 C# 呼叫外部程式,借助 7z.exe 完成任務。不方便之處是我必須先將要打包加密的內容依目錄結構寫成暫存檔,再交給 7-Zip 處理,且做完必須刪檔抹去足跡。這裡隱藏一個危機,若程式在寫暫存檔到刪檔前出錯,便可能殘留明文內容,有資訊外流風險。因此,讓加密壓縮過程完全在記憶體裡完成是較完美的做法。

過去我有示範過用 .NET 4.5 的 ZipArchive、ZipFile 實現 byte[] 不落地壓縮 ZIP 檔,可惜它無法支援加密。放心,.NET 開發社群是很強大的,你想得到的需求幾乎都有解決方案。

針對本次的需求,免費的 DotNetZip Library 開源專案是個好選擇,用 NuGet 即可下載:

DotNetZip 的使用方法十分簡便,現場來個簡單範例。我打算用 C# 動態產一個 Secret.zip 檔案提供本日業績資料,其中包含兩個檔案,index.html 與 imgs\chart.png,ZIP 檔需以密碼加密。這個範例涵蓋了輸出 string 到文字檔、輸出 byte[] 到圖檔、區分資料夾結構、使用密碼加密等功能。

程式碼如下:(我還很無聊地用 Graphics 胡亂畫出長條圖 .png 練手感)

using Ionic.Zip;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var zip = new ZipFile())
            {
                zip.Password = "P@ssW0rd";
                zip.AddEntry("imgs\\chart.png", GenChart());
                zip.AddEntry("index.html", $@"
<html><body>
    <div>{DateTime.Today:yyyy-MM-dd} 業績</div>
    <img src='imgs/chart.png' />
</body>");
                zip.Save("D:\\Secret.zip");
            }
        }

        static byte[] GenChart()
        {
            var bmp = new Bitmap(240, 180);
            var g = Graphics.FromImage(bmp);
            g.FillRectangle(new SolidBrush(Color.LightGray),
                0, 0, bmp.Width, bmp.Height);
            var p = new Pen(new SolidBrush(Color.Black));
            var margin = 6;
            g.DrawRectangle(p, margin, margin, 1, bmp.Height - margin * 2);
            g.DrawRectangle(p, margin, bmp.Height - margin, bmp.Width - margin * 2, 1);
            var colors = new Color[]
            {
                Color.Green,
                Color.Brown,
                Color.BlueViolet,
                Color.Indigo
            };
            var barAreaWidth = (bmp.Width - margin * 2) / 4;
            var barWidth = 40;
            var rnd = new Random();
            Enumerable.Range(0, 4).ToList()
                .ForEach(i =>
                {
                    var value = rnd.Next(120) + 50;
                    g.FillRectangle(new SolidBrush(colors[i]), 
                        margin + i * barAreaWidth + (barAreaWidth - barWidth) / 2, 
                        bmp.Height - margin - value, 
                        barWidth, 
                        value);
                });
            using (var ms = new MemoryStream())
            {
                bmp.Save(ms, ImageFormat.Png);
                return ms.ToArray();
            }
        }
    }
}

如此,我們成功的產生加密 ZIP 檔,具備資料夾結構,過程沒產生任何暫存檔。

網頁內容檢視正常,大成功!

Example of using DotNetZip to compress data in memory to encrypted zip file


Comments

# by Bryan

7-zip 其實有 c# 的 sdk 叫 lzma sdk,請見網址 https://www.7-zip.org/sdk.html

# by Jeffrey

to Bryan,哦哦哦,好物! 感謝分享,有空來研究一下。

# by Leo

我在使用DotNetZip進行加密壓縮時,發現欲加密的檔案為0 byte時,產生的zip沒有被加密,請問黑大有遇過這個問題嗎?

# by Jeffrey

to Leo, 有點不明白,若要壓縮加密的檔案沒有任何內容,應不需要也無法加密,原本預期的結果為何?

# by Leo

to Jeffrey, 假設今日客戶要求每日固定上傳一檔案,名為log.txt,而該檔案必須壓縮加密成zip檔案,但log.txt並不一定會有內容,即使log.txt內無內容,壓縮加密後的檔案log.txt.zip也必須在解壓縮時帶入密碼才能進行解壓縮。 因使用winrar、7zip等軟體都可以達成以上需求,DotNetZip好像沒辦法,後來改用SharpZipLib時解決了這個問題。

# by Jeffrey

to Leo,明白了。ZIP檔內容的檔名跟大小不用密碼便能取得,故理論上不知密碼也能解壓出 0 Byte 檔案。但在你的情境,若沒加密得寫額外邏輯處理無內容情境,會變麻煩。

Post a comment