对 Steam 下载的一次猜想

Steam 下载每次下次分配的硬盘空间通常是很小的,对比一些古董一般的游戏下载,需要将所有的游戏文件下载到本地,之后才能进行解压,这通常需要一倍以上的硬盘空间才能完成。而现代的游戏下载是一边下载一边解压,甚至下载到一部分还进行部分游戏

在这对游戏的分块下载进行一定的猜想和复现。

实现分块压缩

要实现分块下载,就需要将游戏文件进行分割成各种大小不一的压缩包,每个压缩包可以单独解压。为了尽可能的将个个压缩包的大小接近一致,就需要设定一个阈值当将一部分的文件整合后的大小接近该阈值就将这部分的文件进行压缩成一个压缩包。

使用 C# 进行编码,获取文件夹下所有的文件并进行分组,当每个组的大小接近基准值的时候就返回该组。进行编组时有的单体文件本身就大于基准值这时候将该文件直接进行返回,让他一个文件作为一个组,如果对其进行分割的话就达不到每个压缩包可以单独解压的效果了

/// <summary>
/// 将文件分块(每个块总大小尽可能接近指定基准值)
/// </summary>
/// <param name="rootPath">根目录路径</param>
/// <param name="size">基准块大小(默认100MB)</param>
/// <returns>分块结果集合</returns>
public static IEnumerable<Chunk> CreateChunks(string rootPath, long size = 100 * 1024 * 1024) {
	// 遍历文件夹下所有的文件夹并进行封装为 Entity
   var entities = Entity.Entities(rootPath)
       .OrderByDescending(e => e.GetFileInfo().Length) // 大文件优先处理
       .ToList();

   var chunks = new List<Chunk>();
   var currentChunk = new List<Entity>();
   long currentSize = 0;
   var id = 1;

   foreach (var entity in entities) {
       // 处理单个文件超过基准大小的情况
       if (entity.FileSize > size) {
           // next chunk
           if (currentChunk.Count != 0) {
               chunks.Add(new Chunk(rootPath, currentChunk, id++));
               currentChunk =[];
               currentSize = 0;
           }

           // add entity to chunk
           chunks.Add(new Chunk(rootPath, [entity], id++));
           continue;
       }

       // 常规分块逻辑
       if (currentSize + entity.FileSize <= size) {
           currentChunk.Add(entity);
           currentSize += entity.FileSize;
       } else {
           // 判断最小与最大哪个离基准线近
           if (size - currentSize < currentSize + entity.FileSize - size) {
               // 最小的块离基准线近
               chunks.Add(new Chunk(rootPath, currentChunk, id++));
               currentChunk =[entity];
               currentSize = entity.FileSize;
           } else {
               // 最大的块离基准线近
               currentChunk.Add(entity);
               chunks.Add(new Chunk(rootPath, currentChunk, id++));
               currentChunk =[];
               currentSize = 0;
           }
       }
   }

   // 添加最后剩余的块
   if (currentChunk.Count != 0) {
       chunks.Add(new Chunk(rootPath, currentChunk, id));
   }

   return chunks;
}

处理好分组后,就可以对每个组进行压缩了。压缩后会产生多个压缩包,每个压缩包的大小都会尽可能的贴近基准值。


/// <summary>
/// 创建分块并压缩为zip文件
/// </summary>
/// <param name="rootDirPath">要压缩的文件夹路径</param>
/// <param name="outputDirPath">输出目录路径</param>
/// <param name="baseFileName">压缩文件名</param>
/// <param name="size">块大小(最低压缩基准线,如果单体文件大于基准线则单独压缩)</param>
/// <returns>json 结果</returns>
public static string CreateChunksZipFile(string rootDirPath, string? outputDirPath = null, string? baseFileName = null, long size = 512 * 1024 * 1024) {
   if (!Directory.Exists(rootDirPath)) throw new DirectoryNotFoundException("Directory not found.");
   outputDirPath ??= Path.Combine(rootDirPath, "fuck_zip_output");
   if (!Directory.Exists(outputDirPath)) Directory.CreateDirectory(outputDirPath);

   var start = DateTime.Now;
   var chunks = Chunk.CreateChunks(rootDirPath, size);
   var rootJson = new JsonObject();
   var rootJsonArray = new JsonArray();
   var entityCount = 0;
   var totalSize = 0L;
   foreach (var chunk in chunks) {
       if (baseFileName != null) chunk.BaseFileName = baseFileName;
       Console.WriteLine(chunk);
       entityCount += chunk.Entities.Count();
       totalSize += chunk.TotalSize;
       // 性能严重消耗处
       var zipArchive = chunk.ZipArchive(outputDirPath);

       var json = new JsonObject{
           { "id", chunk.Id },
           { "fileName", chunk.GetFileName() },
           { "entityCount", entityCount },
           { "size", chunk.TotalSize }
       };
       var entityJsonArray = new JsonArray();
       foreach (var entity in chunk.Entities) {
           var json2 = new JsonObject{
               { "path", entity.RelativeFilePath },
               { "hashcode", entity.GetHashcode() } // 性能严重消耗处
           };
           entityJsonArray.Add(json2);
       }

       json.Add("entities", entityJsonArray);
       // 性能严重消耗处
       json.Add("hashcode", HashCodeUtil.CalculateFileHash(zipArchive));
       rootJsonArray.Add(json);
   }

   rootJson.Add("chunks", rootJsonArray);
   rootJson.Add("count", rootJsonArray.Count);
   rootJson.Add("totalSize", totalSize);
   rootJson.Add("entityCount", entityCount);
   rootJson.Add("date", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
   rootJson.Add("_output", outputDirPath);
   rootJson.Add("_timeConsuming", DateTime.Now.Subtract(start).TotalSeconds);
   rootJson.Add("_input", rootDirPath);
   // 生成最终 JSON 字符串(带缩进)
   var finalJson = rootJson.ToJsonString(new JsonSerializerOptions{
       WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
   });

   // 写入文件
   var outputPath = Path.Combine(outputDirPath, "result.json");
   File.WriteAllText(outputPath, finalJson);
   Console.WriteLine($"{outputPath} created.");
   return finalJson;
}
### 如何下载Steam客户端或其内容 下载Steam客户端或其相关内容可以通过以下方式完成: #### 1. 官方下载地址 用户可以从Steam官方网站直接下载最新的客户端安装包。确保访问的是官方链接以避免安全风险[^1]。对于Windows系统,访问 [Steam官网](https://store.steampowered.com/) 并点击“安装Steam”按钮即可开始下载。 #### 2. 离线安装包 如果需要离线安装包,可以参考第三方资源站点提供的最新版本离线安装文件。例如,GitCode上提供了2022年的Steam客户端离线安装包。请确保从可信来源获取,以防止潜在的安全问题。 #### 3. Linux系统下的安装 对于Linux用户,可以通过Steam官方提供的deb包进行安装[^5]。此外,还需要注意一些特定的配置要求,例如禁用DRI3以确保Steam正常运行。可以通过在终端中运行以下命令来安装Steam: ```bash sudo apt update sudo apt install steam ``` #### 4. DLL修复与客户端文件完整性验证 如果在安装或使用过程中遇到`steamclient.dll`损坏的问题,可以按照以下方法解决[^2]: - 在Steam客户端内选择“帮助” > “验证Steam客户端文件完整性”。 - 如果问题仍未解决,可以尝试重新安装Steam客户端或手动替换损坏的DLL文件。 #### 5. 开源替代方案 对于希望使用开源替代品的用户,可以考虑OpenSteamClient项目[^3]。这是一个部分开源的Steam客户端替代方案,专为Linux用户设计。虽然功能可能不如官方客户端全面,但对于特定需求的用户来说是一个不错的选择。 #### 6. 轻量级框架 对于开发者或希望自定义Steam客户端功能的用户,可以使用Vapor框架[^4]。这是一个基于Node.js的轻量级Steam客户端框架,允许用户编写扩展程序并实现自动化任务。 ### 示例代码:验证Steam客户端文件完整性 以下是一个简单的脚本,用于通过Steam API验证客户端文件完整性(仅作参考): ```python import subprocess def validate_steam_files(): try: subprocess.run(["steam", "-validate"], check=True) print("Steam客户端文件验证已完成。") except subprocess.CalledProcessError as e: print(f"验证过程中出现错误: {e}") validate_steam_files() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值