// 指定的文件清单复制到本地目录 public static IEnumerator CopyStreamingAssets(string outputPath, FileListManifest fileListManifest, Action oncomplete) { var count = fileListManifest.files.Count; for (var i = 0; i < count; i++) { var file = fileListManifest.files[i]; var outputFile = Path.Combine(outputPath, file.name); if (!File.Exists(outputFile)) { var streamingAssetsFilePath = GetStreamingAssetsFilePath(file.name); var uwr = UnityWebRequest.Get(streamingAssetsFilePath); yield return(uwr.SendWebRequest()); if (uwr.error == null && uwr.responseCode == 200) { try { var bytes = uwr.downloadHandler.data; var checksum = Crc16.ComputeChecksum(bytes); var size = bytes.Length; var metaFile = Path.Combine(outputPath, file.name + Metadata.Ext); var metadata = new Metadata() { checksum = Crc16.ToString(checksum), size = size, }; var metaJson = JsonUtility.ToJson(metadata); File.WriteAllText(metaFile, metaJson); File.WriteAllBytes(outputFile, bytes); } catch (Exception exception) { Debug.LogWarningFormat("CopyStreamingAssets exception: {0}\n{1}", streamingAssetsFilePath, exception); } } else { Debug.LogWarningFormat("CopyStreamingAssets request failed {0}: {1} {2}", streamingAssetsFilePath, uwr.responseCode, uwr.error); } } } oncomplete?.Invoke(); }
private void _HttpDownload(string url, int partialSize, byte[] buffer, Utils.Crc16 crc, Stream targetStream, int timeout) { PrintDebug($"downloading from {url}"); var uri = new Uri(url); var req = WebRequest.CreateHttp(uri); req.Method = WebRequestMethods.Http.Get; req.ContentType = BundleContentType; req.ReadWriteTimeout = 10000; if (timeout > 0) { req.Timeout = timeout * 1000; } if (partialSize > 0) { req.AddRange(partialSize); } using (var rsp = req.GetResponse()) { using (var webStream = rsp.GetResponseStream()) { var recvAll = 0L; while (recvAll < rsp.ContentLength) { var recv = webStream.Read(buffer, 0, buffer.Length); if (recv > 0 && _running) { recvAll += recv; targetStream.Write(buffer, 0, recv); crc.Update(buffer, 0, recv); _progress = Mathf.Clamp01((float)(recvAll + partialSize) / _size); if (_slow > 0) { Thread.Sleep(_slow); // 模拟低速下载 } // PrintDebug($"{recvAll + partialSize}, {_size}, {_progress}"); } else { break; } } } } // PrintDebug($"download exited"); }
// 打开指定的清单文件 (带校验) public static Manifest ParseManifestFile(string filePath, FileEntry fileEntry, string password) { if (File.Exists(filePath)) { using (var fs = File.OpenRead(filePath)) { var crc = new Crc16(); crc.Update(fs); if (crc.hex == fileEntry.checksum && fs.Length == fileEntry.size) { fs.Seek(0, SeekOrigin.Begin); return(ParseManifestStream(fs, fileEntry, password)); } } } return(null); }
private void DownloadExec(object state) { var buffer = new byte[BufferSize]; var tempPath = _finalPath + PartExt; var metaPath = _finalPath + Metadata.Ext; var retry = 0; FileStream fileStream = null; while (true) { string error = null; var crc = new Utils.Crc16(); var partialSize = 0; var success = true; if (fileStream == null) { try { if (File.Exists(tempPath)) // 处理续传 { var fileInfo = new FileInfo(tempPath); fileStream = fileInfo.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite); partialSize = (int)fileInfo.Length; if (partialSize > _size) // 目标文件超过期望大小, 直接废弃 { fileStream.SetLength(0); partialSize = 0; } else if (partialSize <= _size) // 续传 { crc.Update(fileStream); PrintDebug($"partial check {partialSize} && {_size} ({crc.hex})"); } } else // 创建下载文件 { fileStream = File.Open(tempPath, FileMode.OpenOrCreate, FileAccess.ReadWrite); fileStream.SetLength(0); } } catch (Exception exception) { // PrintError($"file exception: {exception}"); error = $"file exception: {exception}"; success = false; } } else { fileStream.SetLength(0L); } if (success && (_size <= 0 || partialSize < _size)) { try { _HttpDownload(this.url, partialSize, buffer, crc, fileStream, _timeout); } catch (Exception exception) { // PrintError($"network exception: {exception}"); error = $"network exception: {exception}"; success = false; } } if (success && fileStream.Length != _size) { if (_size > 0) { // PrintError($"filesize exception: {fileStream.Length} != {_size}"); error = $"wrong file size: {fileStream.Length} != {_size}"; success = false; } else { _size = (int)fileStream.Length; } } else if (success && crc.hex != _checksum) { if (_checksum != null) { // PrintError($"checksum exception: {crc.hex} != {_checksum}"); error = $"corrupted file: {crc.hex} != {_checksum}"; success = false; } else { _checksum = crc.hex; } } lock (this) { if (_isDone || _destroy) { success = false; } } if (success) { try { // _WriteStream(buffer, fileStream, finalPath); fileStream.Close(); fileStream = null; if (File.Exists(_finalPath)) { File.Delete(_finalPath); } File.Move(tempPath, _finalPath); _WriteMetadata(metaPath); Complete(null); // PrintDebug("download succeeded"); break; } catch (Exception exception) { // PrintError($"write exception: {exception}"); error = $"write exception: {exception}"; success = false; } } if (!Retry(++retry)) { if (fileStream != null) { fileStream.Close(); fileStream = null; } Complete(error ?? "unknown error"); PrintError($"[stop] download failed ({error})"); break; } Thread.Sleep(1000); PrintError($"[retry] ({_destroy}) download failed ({error})"); } PrintDebug("download task thread exited"); }