2022年底c# 解壓zip文件遇到的一個bug

2022年底c# 解壓zip文件遇到的一個bug

最近在排查一個上傳功能時,客戶端上傳的是zip文件,到伺服器端後使用c# 解壓zip文件的代碼將上傳文件解壓後驗證是否是允許上傳的文件類型,並且要驗證亂改文件後綴啊,文件頭什麼的都要走一遭,結果解壓zip文件時就出妖蛾子了。

最后更新 2022/12/23 上午9:39
江湖人士
预计阅读 3 分钟
分类
.NET
标签
.NET C#

本文由網友投稿。

作者:江湖人士

原文標題:2022 年底 c# 解壓 zip 文件遇到的一個 bug

原文連結:https://jhrs.com/2022/46060.html

最近在排查一個上傳功能時,客戶端上傳的是 zip 文件,到伺服器端後使用 c# 解壓 zip 文件的代碼將上傳文件解壓後驗證是否是允許上傳的文件類型,並且要驗證亂改文件後綴啊,文件頭什麼的都要走一遭,結果解壓 zip 文件時就出妖蛾子了。

c# 解壓 zip 文件

先说一下前文(或者上下文),在 IIS 上部署了一个文件服务站点,用于上传各类文件,流程上是先上传到站点根目录里面随机创建的一个临时目录(这里采用偷懒方案,直接使用 guid 作为目录名创建),先通过文件验证后再将其通过代码剪切或者复制到正式存档目录,C# 复制或者移动文件的代码可以参考江湖人士网的这篇文章。昨天快下班时发现上传 zip 文件时报错,在文件服务根站点创建了很多很多的 guid 开头的目录,我的妹呀,这下玩犊子了,事出反常必有妖啊,肯定代码出错了。

有 bug 的解壓代碼

這都馬上 2022 年底了,出了這 bug 後,趕緊搭建個模擬環境跑一下,發現如下原來的代碼確實有問題,原始代碼如下:

/// <summary>
/// 解压文件
/// </summary>
/// <param name="saveDir">保存目录</param>
/// <param name="stream"></param>
public static void UnZipFiles(string saveDir, Stream stream)
{
    using (ZipInputStream s = new ZipInputStream(stream))
    {
        ZipEntry theEntry;
        while ((theEntry = s.GetNextEntry()) != null)
        {
            string directoryName = $"{saveDir}{Path.GetDirectoryName(theEntry.Name)}\\";
            string fileName = Path.GetFileName(theEntry.Name);
            Directory.CreateDirectory(directoryName);
            using (FileStream streamWriter = File.Create(directoryName + fileName))
            {
                byte[] data = new byte[2048];
                while (true)
                {
                    int size = s.Read(data, 0, data.Length);
                    if (size > 0)
                    {
                        streamWriter.Write(data, 0, size);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
    }
}

对了,这里需要说明一下,这是以前的老项目,因此压缩,解压使用了 ICSharpCode.SharpZipLib.Zip 这个组件。

當打開源碼來看,一眼就發現了問題所在,邏輯不嚴謹導致,解壓文件保存目錄直接拼接。

如何修復此 bug?

知道了问题所在,修复自然简单,调用Path.Combine方法即可,解压时再判断一下是目录还是文件即可,最终修复后的代码如下:

/// <summary>
/// 解压文件
/// </summary>
/// <param name="saveDir">保存目录</param>
/// <param name="stream"></param>
public static void UnZipFiles(string saveDir, Stream stream)
{
    using (ZipInputStream s = new ZipInputStream(stream))
    {
        ZipEntry theEntry;
        string directoryName, file, fileName;
        while ((theEntry = s.GetNextEntry()) != null)
        {
            directoryName = Path.Combine(saveDir, Path.GetDirectoryName(theEntry.Name));
            fileName = Path.GetFileName(theEntry.Name);
            Directory.CreateDirectory(directoryName);
            file = Path.Combine(directoryName, fileName);

            if (theEntry.IsFile)
            {
                using (FileStream streamWriter = File.Create(file))
                {
                    byte[] data = new byte[2048];
                    while (true)
                    {
                        int size = s.Read(data, 0, data.Length);
                        if (size > 0)
                        {
                            streamWriter.Write(data, 0, size);
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
        }
    }
}
Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2026/2/7

aot使用經驗總結

從項目創建伊始,就應養成良好的習慣,即只要添加了新功能或使用了較新的語法,就及時進行 aot 發布測試。

继续阅读