C#中DES加密解密问题、压缩解压缩文件、进度条以及WinForm中使用控制台的解决方案

最近在做一项目,是一个关于分布式信息采集系统的,这个项目可以说是我上大学两年来做过的最完整最难的项目了,其中涉及到的问题很多很多,很遗憾的是这个项目中很多内容都不是我自己写的,而是实在是自己找不到解决方法,然后从网上找到方法,然后再自己加以改进,整合到自己的代码中并完善使唤其符合自己的需求。由于在做个项目时我主要负责网络传输及加密解密和压缩解压缩的问题,所以就只对他们进行总结。而团队的另一个人则是解决数据库问题的,说实话,他做的比我还难,因为对数据库的要求太高了,而且我们才刚上大三,对数据库还不能很熟练地进行相关的操作。

一、DES加密解密问题

首先说一下DES加密解密的问题吧,我是参考 http://www.zu14.cn/2009/05/26/csharp-des-library-support-file-base64-encoding/ 这个网址里的方法做的,现在把代码贴在这里,以防以后再用到的时候找不到了。

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security;
using System.Security.Cryptography;
/*----------------------------------------------
 *  DES加密、解密类库,字符串加密结果使用BASE64编码返回,支持文件的加密和解密
 *  作者: 三角猫/DeltaCat
 *  网址: http://www.zu14.cn
 *  转载务必保留此信息
 * ---------------------------------------------
 */
namespace ZU14
{
    public sealed class DES
    {
        string iv = "1234的yzo";
        string key = "123在yzo";
        /// <summary>
        /// DES加密偏移量,必须是>=8位长的字符串
        /// </summary>
        public string IV
        {
            get { return iv; }
            set { iv = value; }
        }
        /// <summary>
        /// DES加密的私钥,必须是8位长的字符串
        /// </summary>
        public string Key
        {
            get { return key; }
            set { key = value; }
        }
        /// <summary>
        /// 对字符串进行DES加密
        /// </summary>
        /// <param name="sourceString">待加密的字符串</param>
        /// <returns>加密后的BASE64编码的字符串</returns>
        public string Encrypt(string sourceString)
        {
            byte[] btKey = Encoding.Default.GetBytes(key);
            byte[] btIV = Encoding.Default.GetBytes(iv);
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            using (MemoryStream ms = new MemoryStream())
            {
                byte[] inData = Encoding.Default.GetBytes(sourceString);
                try
                {
                    using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(btKey, btIV), CryptoStreamMode.Write))
                    {
                        cs.Write(inData, 0, inData.Length);
                        cs.FlushFinalBlock();
                    }
                    return Convert.ToBase64String(ms.ToArray());
                }
                catch
                {
                    throw;
                }
            }
        }
        /// <summary>
        /// 对DES加密后的字符串进行解密
        /// </summary>
        /// <param name="encryptedString">待解密的字符串</param>
        /// <returns>解密后的字符串</returns>
        public string Decrypt(string encryptedString)
        {
            byte[] btKey = Encoding.Default.GetBytes(key);
            byte[] btIV = Encoding.Default.GetBytes(iv);
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            using (MemoryStream ms = new MemoryStream())
            {
                byte[] inData = Convert.FromBase64String(encryptedString);
                try
                {
                    using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(btKey, btIV), CryptoStreamMode.Write))
                    {
                        cs.Write(inData, 0, inData.Length);
                        cs.FlushFinalBlock();
                    }
                    return Encoding.Default.GetString(ms.ToArray());
                }
                catch
                {
                    throw;
                }
            }
        }
        /// <summary>
        /// 对文件内容进行DES加密
        /// </summary>
        /// <param name="sourceFile">待加密的文件绝对路径</param>
        /// <param name="destFile">加密后的文件保存的绝对路径</param>
        public void EncryptFile(string sourceFile, string destFile)
        {
            if (!File.Exists(sourceFile)) throw new FileNotFoundException("指定的文件路径不存在!", sourceFile);
            byte[] btKey = Encoding.Default.GetBytes(key);
            byte[] btIV = Encoding.Default.GetBytes(iv);
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            byte[] btFile = File.ReadAllBytes(sourceFile);
            using (FileStream fs = new FileStream(destFile, FileMode.Create, FileAccess.Write))
            {
                try
                {
                    using (CryptoStream cs = new CryptoStream(fs, des.CreateEncryptor(btKey, btIV), CryptoStreamMode.Write))
                    {
                        cs.Write(btFile, 0, btFile.Length);
                        cs.FlushFinalBlock();
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    fs.Close();
                }
            }
        }
        /// <summary>
        /// 对文件内容进行DES加密,加密后覆盖掉原来的文件
        /// </summary>
        /// <param name="sourceFile">待加密的文件的绝对路径</param>
        public void EncryptFile(string sourceFile)
        {
            EncryptFile(sourceFile, sourceFile);
        }
        /// <summary>
        /// 对文件内容进行DES解密
        /// </summary>
        /// <param name="sourceFile">待解密的文件绝对路径</param>
        /// <param name="destFile">解密后的文件保存的绝对路径</param>
        public void DecryptFile(string sourceFile, string destFile)
        {
            if (!File.Exists(sourceFile)) throw new FileNotFoundException("指定的文件路径不存在!", sourceFile);
            byte[] btKey = Encoding.Default.GetBytes(key);
            byte[] btIV = Encoding.Default.GetBytes(iv);
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            byte[] btFile = File.ReadAllBytes(sourceFile);
            using (FileStream fs = new FileStream(destFile, FileMode.Create, FileAccess.Write))
            {
                try
                {
                    using (CryptoStream cs = new CryptoStream(fs, des.CreateDecryptor(btKey, btIV), CryptoStreamMode.Write))
                    {
                        cs.Write(btFile, 0, btFile.Length);
                        cs.FlushFinalBlock();
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    fs.Close();
                }
            }
        }
        /// <summary>
        /// 对文件内容进行DES解密,加密后覆盖掉原来的文件
        /// </summary>
        /// <param name="sourceFile">待解密的文件的绝对路径</param>
        public void DecryptFile(string sourceFile)
        {
            DecryptFile(sourceFile, sourceFile);
        }
    }
}

可以把下面的测试代码放入到Main函数里进行测试。

ZU14.DES des = new ZU14.DES();
des.IV = "abcd哈哈笑";
des.Key = "必须八位";
string es = des.Encrypt("在");
Console.WriteLine(es);
Console.Write(des.Decrypt(es));
des.EncryptFile(@"d:\a.txt", @"d:\b.txt");
des.DecryptFile(@"d:\b.txt");
Console.ReadKey(true);

二、压缩解压文件问题

再接下来就是压缩和解压缩文件的问题了。我是参考 http://hi.baidu.com/ld_hust/blog/item/2de82b1f17483c174034176d.html 这个网址里方法。代码如下:

添加引用,添加SharpZipLib.dll

SharpZipLib.dll 的下载网址:http://www.icsharpcode.net/OpenSource/SharpZipLib/

//***************CodeTest*************************************
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.GZip;
class Test
{
    //This is the method of zip:
    //SrcFile 源文件路径,DstFile 压缩后目标文件的路径,BufferSize 缓冲期大小
    public static void Zip(string SrcFile, string DstFile, int BufferSize)
    {
        FileStream fileStreamIn = new FileStream(SrcFile, FileMode.Open, FileAccess.Read);
        FileStream fileStreamOut = new FileStream(DstFile, FileMode.Create, FileAccess.Write);
        ZipOutputStream zipOutStream = new ZipOutputStream(fileStreamOut);
        byte[] buffer = new byte[BufferSize];
        ZipEntry entry = new ZipEntry(Path.GetFileName(SrcFile));
        zipOutStream.PutNextEntry(entry);
        int size;
        do
        {
            size = fileStreamIn.Read(buffer, 0, buffer.Length);
            zipOutStream.Write(buffer, 0, size);
        } while (size > 0);
        zipOutStream.Close();
        fileStreamOut.Close();
        fileStreamIn.Close();
    }
    //This is the method of unzip:
    //SrcFile 源文件路径,DstFile 解压缩后目标文件的路径,BufferSize 缓冲期大小
    public static void UnZip(string SrcFile, string DstFile, int BufferSize)
    {
        FileStream fileStreamIn = new FileStream(SrcFile, FileMode.Open, FileAccess.Read);
        ZipInputStream zipInStream = new ZipInputStream(fileStreamIn);
        ZipEntry entry = zipInStream.GetNextEntry();
        FileStream fileStreamOut = new FileStream(DstFile, FileMode.Create, FileAccess.Write);
        int size;
        byte[] buffer = new byte[BufferSize];
        do
        {
            size = zipInStream.Read(buffer, 0, buffer.Length);
            fileStreamOut.Write(buffer, 0, size);
        } while (size > 0);
        zipInStream.Close();
        fileStreamOut.Close();
        fileStreamIn.Close();
    }
    public static void Main(string[] args)
    {
        try
        {
            //定义文件路径
            string SrcFile = @"C:\temp\MyTest.txt";
            string DstFile = @"C:\temp\MyTest.zip";
            //默认缓冲器字节大小
            int BufferSize = 4096;
            //压缩文件,将 SrcFile 文件压缩为 DstFile 文件
            Zip(SrcFile, DstFile, BufferSize);
            //解压缩文件,将压缩文件 DstFile 解压为 SrcFile 文件 
            //UnZip(DstFile, SrcFile, BufferSize);
        }
        catch (IOException ex)
        {
            Console.WriteLine("An IO exception has been thrown!");
            Console.WriteLine(ex.ToString());
            Console.ReadKey();
        }
    }
}
//***************EndCode*************************************
GZipStream 类:提供用于压缩和解压缩流的方法和属性。
//***************CodeTest************************************
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.IO.Compression;//使用压缩类
class Test
{
    static void SaveCompressedFile(string filename, string data)
    {
        //创建一个 FileStream 对象
        FileStream fileStream = new FileStream(filename, FileMode.Create, FileAccess.Write);
        //创建一个 GZipStream 对象,Compress 表示压缩基础流。
        GZipStream compressionStream = new GZipStream(fileStream, CompressionMode.Compress);
        //实现一个 write,使其以一种特定的编码向流中写入字符。
        StreamWriter writer = new StreamWriter(compressionStream);
        writer.Write(data);
        writer.Close();
    }
    static string LoadCompressedFile(string filename)
    {
        //创建一个 FileStream 对象
        FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
        //创建一个 GZipStream 对象,Decompress 表示解压缩基础流。
        GZipStream compressionStream = new GZipStream(fileStream, CompressionMode.Decompress);
        //实现从字符串进行读取
        StreamReader reader = new StreamReader(compressionStream);
        string data = reader.ReadToEnd();
        reader.Close();
        return data;
    }
    public static void Main(string[] args)
    {
        try
        {
            //定义文件路径
            string filename = @"c:\compressedFile.txt";
            Console.WriteLine("Enter a string to compress (will be repeated 100 times):");
            //再控制台上输入文字
            string sourceString = Console.ReadLine();
            StringBuilder sourceStringMultiplier = new StringBuilder(sourceString.Length * 100);
            for (int i = 0; i < 100; i++)
            {
                sourceStringMultiplier.Append(sourceString);
            }
            sourceString = sourceStringMultiplier.ToString();
            Console.WriteLine("Source data is {0} bytes long.", sourceString.Length);
            SaveCompressedFile(filename, sourceString);
            Console.WriteLine("\nData saved to {0}.", filename);
            FileInfo compressedFileData = new FileInfo(filename);
            Console.WriteLine("Compressed file is {0} bytes long.", compressedFileData.Length);
            string recoveredString = LoadCompressedFile(filename);
            recoveredString = recoveredString.Substring(0, recoveredString.Length / 100);
            Console.WriteLine("\nRecovered data: {0}", recoveredString);
            Console.ReadKey();
        }
        catch (IOException ex)
        {
            Console.WriteLine("An IO exception has been thrown!");
            Console.WriteLine(ex.ToString());
            Console.ReadKey();
        }
    }
}
//***************EndCode*************************************
//***************CodeTest*************************************
//***************EndCode*************************************

三、进度条问题

再接下就是进度条的问题了,分为两部分,一部分是在控制台是显示进度条,另一部分是在窗体中显示。

先说一下在控制台中显示的方法,我是参考 http://www.cnblogs.com/xuf22/archive/2009/03/13/1410645.html 这个网址里的方法。代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TextProgressing
{
    class Program
    {
        static void Main(string[] args)
        {
            bool isBreak = false;
            ConsoleColor colorBack = Console.BackgroundColor; ConsoleColor colorFore = Console.ForegroundColor;
            //第一行信息             
            Console.WriteLine("*********** jinjazz now working******");
            //第二行绘制进度条背景             
            Console.BackgroundColor = ConsoleColor.DarkCyan; for (int i = 0; ++i <= 25; ) { Console.Write(" "); } Console.WriteLine(" "); Console.BackgroundColor = colorBack;
            //第三行输出进度             
            Console.WriteLine("0%");
            //第四行输出提示,按下回车可以取消当前进度
            Console.WriteLine("<Press Enter To Break.>");
            //-----------------------上面绘制了一个完整的工作区域,下面开始工作
            //开始控制进度条和进度变化 
            for (int i = 0; ++i <= 100; )
            {
                //先检查是否有按键请求,如果有,判断是否为回车键,如果是则退出循环 
                if (Console.KeyAvailable && System.Console.ReadKey(true).Key == ConsoleKey.Enter)
                {
                    isBreak = true;
                    break;
                }                 //绘制进度条进度                
                Console.BackgroundColor = ConsoleColor.Yellow;//设置进度条颜色                 
                Console.SetCursorPosition(i / 4, 1);
                //设置光标位置,参数为第几列和第几行                 
                Console.Write(" ");//移动进度条                 
                Console.BackgroundColor = colorBack;//恢复输出颜色
                //更新进度百分比,原理同上.                 
                Console.ForegroundColor = ConsoleColor.Green;
                Console.SetCursorPosition(0, 2);
                Console.Write("{0}%", i);
                Console.ForegroundColor = colorFore;
                //模拟实际工作中的延迟,否则进度太快                 
                System.Threading.Thread.Sleep(100);
            }
            //工作完成,根据实际情况输出信息,而且清楚提示退出的信息     
            Console.SetCursorPosition(0, 3);
            Console.Write(isBreak ? "break!!!" : "finished.");
            Console.WriteLine(" ");
            //等待退出             
            Console.ReadKey(true);
        }
    }
 }

接下来再说一下窗体中显示进度条的方法,我是参考 http://blog.i1981.com/?p=16 这个网址里的,代码如下:

using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsApplication1
{
     /// <summary>
     /// Form1 类
     /// </summary>
     public partial class Form1 : Form
     {
         public Form1()
         {
             InitializeComponent();
         }
         private void button1_Click(object sender, EventArgs e)
         {
             //用子线程工作
             new System.Threading.Thread(new System.Threading.ThreadStart(StartDownload)).Start();
         }
         //开始下载
         public void StartDownload()
         {
             Downloader downloader = new Downloader();
             downloader.onDownLoadProgress += new Downloader.dDownloadProgress(downloader_onDownLoadProgress);
             downloader.Start();
         }
         //同步更新UI
         void downloader_onDownLoadProgress(long total, long current)
         {
             if (this.InvokeRequired)
             {
                 this.Invoke(new Downloader.dDownloadProgress(downloader_onDownLoadProgress), new object[] { total, current });
             }
             else
             {
                 this.progressBar1.Maximum = (int)total;
                 this.progressBar1.Value = (int)current;
             }
         }
     }
     /// <summary>
     /// 下载类(您的复杂处理类)
     /// </summary>
     public class Downloader
     {
         //委托
         public delegate void dDownloadProgress(long total,long current);
         //事件
         public event dDownloadProgress onDownLoadProgress;
         //开始模拟工作
         public void Start()
         {
             for (int i = 0; i < 100; i++)
             {
                 if (onDownLoadProgress != null)
                     onDownLoadProgress(100, i);
                 System.Threading.Thread.Sleep(100);
             }
         }
     }
}

另一种方法:转自:CSDN 寓翁专栏

最近看了好多人问这方面的问题,以前我也写过一篇blog,里面说了如何在子线程中控制进度条。但目前大多数环境,需要弹出模式窗口,来显示进度条,那么只需要在原先的基础上稍作修改即可。

首先是进度条窗体,需要在上面添加进度条,然后去掉ControlBox。除此外,还要增加一个方法,用来控制进度条的增加幅度,具体如下:

     /// <summary>
    /// Increase process bar
    /// </summary>
    /// <param name=”nValue”>the value increased</param>
    /// <returns></returns>
    public bool Increase( int nValue )
    {
        if( nValue > 0 )
        {
            if( prcBar.Value + nValue < prcBar.Maximum )
            {
                prcBar.Value += nValue;
                return true;
            }
            else
            {
                prcBar.Value = prcBar.Maximum;
                this.Close();
                return false;
            }
        }
        return false;
    }

接着就是主窗体了,如何进行操作了,首先需要定义两个私有成员,一个委托。其中一个私有成员是保存当前进度条窗体对象,另一个是保存委托方法(即增加进度条尺度),具体如下:

private frmProcessBar myProcessBar = null;
private delegate bool IncreaseHandle( int nValue ); 
private IncreaseHandle myIncrease = null;

接着要在主窗体中提供函数来打开进度条窗体,如下:

     /// <summary>
    /// Open process bar window
    /// </summary>
    private void ShowProcessBar()
    {
        myProcessBar = new frmProcessBar();
        // Init increase event
        myIncrease = new IncreaseHandle( myProcessBar.Increase );
        myProcessBar.ShowDialog();
        myProcessBar = null;
    }

那么现在就可以开始创建线程来运行,具体如下:

     /// <summary>
    /// Sub thread function
    /// </summary>
    private void ThreadFun()
    {
        MethodInvoker mi = new MethodInvoker( ShowProcessBar );
        this.BeginInvoke( mi );
        Thread.Sleep( 1000 );//Sleep a while to show window
        bool blnIncreased = false;
        object objReturn = null;
        do
        {
               Thread.Sleep( 50 );
               objReturn = this.Invoke( this.myIncrease,
               new object[]{ 2 } );
               blnIncreased = (bool)objReturn ;
        }
        while( blnIncreased );
    }

注意以上,在打开进度条窗体和增加进度条进度的时候,一个用的是BeginInvoke,一个是Invoke,这里的区别是BeginInvoke不需要等待方法运行完毕,而Invoke是要等待方法运行完毕。还有一点,此处用返回值来判断进度条是否到头了,如果需要有其他的控制,可以类似前面的方法来进行扩展。

启动线程,可以如下:

Thread thdSub = new Thread( new ThreadStart( ThreadFun ) );
thdSub.Start();

这样,一个用模式打开进度条窗体就做完了。

四、窗体中使用控制台的问题

这个是我在做的时候,本来想在窗体中使用上面介绍的进度条,结果由于各种各样的原因,我放弃了这种方法,想改成在控制台中显示进度条的方法,于是就找到这篇文章 http://hi.baidu.com/sageking2/blog/item/1750242a241e702ad42af19d.html ,代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace 在WinForm中启动控制台
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        [DllImport("Kernel32.dll")]
        static extern bool AllocConsole();
        [DllImport("Kernel32.dll")]
        static extern bool FreeConsole();
        private void button1_Click(object sender, EventArgs e)
        {
            AllocConsole();
            Console.WriteLine("hello");
            //FreeConsole();释放掉
        }
    }
}

至此,我所的所有问题都已经解决了,项目终于完成了,终于可以开始认真学习Java语言及Android开发了……