// 打开指定的清单文件 (带校验) public static bool OpenManifestFile(string filePath, string checksum, out Manifest manifest) { if (File.Exists(filePath)) { using (var fs = File.OpenRead(filePath)) { var crc = new Crc16(); crc.Update(fs); if (crc.hex == checksum) { fs.Seek(0, SeekOrigin.Begin); var bytes = new byte[fs.Position]; var read = fs.Read(bytes, 0, bytes.Length); if (read == bytes.Length) { var json = Encoding.UTF8.GetString(bytes); manifest = JsonUtility.FromJson <Manifest>(json); return(manifest != null); } } } } manifest = null; return(false); }
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"); }
private void ProcessJob(JobInfo jobInfo) { Debug.LogFormat("processing job: {0} ({1})", jobInfo.name, jobInfo.comment); var tempPath = jobInfo.path + PartExt; if (_fileStream != null) { _fileStream.Close(); _fileStream = null; } while (true) { string error = null; var partialSize = 0; var success = true; var wsize = jobInfo.size; var wchecksum = jobInfo.checksum; _crc.Clear(); if (_fileStream == null) { try { var fileInfo = new FileInfo(tempPath); if (fileInfo.Exists) // 处理续传 { _fileStream = fileInfo.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite); partialSize = (int)fileInfo.Length; if (partialSize > jobInfo.size) // 目标文件超过期望大小, 直接废弃 { _fileStream.SetLength(0); partialSize = 0; } else if (partialSize <= jobInfo.size) // 续传 { _crc.Update(_fileStream); Debug.LogFormat("partial check {0} && {1} ({2})", partialSize, jobInfo.size, _crc.hex); } } else // 创建下载文件 { if (!Directory.Exists(fileInfo.DirectoryName)) { Directory.CreateDirectory(fileInfo.DirectoryName); } _fileStream = File.Open(tempPath, FileMode.OpenOrCreate, FileAccess.ReadWrite); _fileStream.SetLength(0); } } catch (Exception exception) { Debug.LogErrorFormat("file exception: {0}\n{1}", jobInfo.path, exception); error = $"file exception: {exception}"; success = false; } } else { _fileStream.SetLength(0L); } if (success && (jobInfo.size <= 0 || partialSize < jobInfo.size)) { var url = GetUrl(jobInfo); try { 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; } if (partialSize > 0) { req.AddRange(partialSize); } using (var rsp = req.GetResponse()) { using (var webStream = rsp.GetResponseStream()) { var recvAll = 0L; var recvCalc = 0L; var stopwatch = new Stopwatch(); stopwatch.Start(); while (recvAll < rsp.ContentLength) { var _bpms = Math.Max(1, jobInfo.bytesPerSecond / 10); var recv = webStream.Read(_buffer, 0, Math.Min(_bpms, _buffer.Length)); if (recv > 0 && !_destroy) { recvCalc += recv; if (recvCalc >= _bpms) { var millisecs = stopwatch.ElapsedMilliseconds; var delay = (int)(100.0 * recvCalc / _bpms - millisecs); // Debug.LogFormat("net ++ {0} {1} sbps {2} recv {3}", delay, millisecs, _bpms, recvCalc); if (delay > 0) { Thread.Sleep(delay); } stopwatch.Restart(); recvCalc -= _bpms; } recvAll += recv; _fileStream.Write(_buffer, 0, recv); _crc.Update(_buffer, 0, recv); jobInfo.bytes = (int)(recvAll + partialSize); // PrintDebug($"{recvAll + partialSize}, {_size}, {_progress}"); } else { break; } } } } } catch (Exception exception) { Debug.LogErrorFormat("network exception: {0}\n{1}", url, exception); error = $"network exception: {exception}"; success = false; } } if (success && _fileStream.Length != jobInfo.size) { if (jobInfo.size > 0) { error = string.Format("filesize exception: {0} {1} != {2}", jobInfo.name, _fileStream.Length, jobInfo.size); Debug.LogError(error); success = false; } else { wsize = (int)_fileStream.Length; } } if (success && _crc.hex != jobInfo.checksum) { if (!string.IsNullOrEmpty(jobInfo.checksum)) { error = string.Format("corrupted file: {0} {1} != {2}", jobInfo.name, _crc.hex, jobInfo.checksum); Debug.LogError(error); success = false; } else { wchecksum = _crc.hex; } } if (_destroy) { success = false; } if (success) { try { // _WriteStream(buffer, fileStream, finalPath); _fileStream.Close(); _fileStream = null; if (File.Exists(jobInfo.path)) { File.Delete(jobInfo.path); } File.Move(tempPath, jobInfo.path); // 写入额外的 meta var meta = new Metadata() { checksum = wchecksum, size = wsize, }; var json = JsonUtility.ToJson(meta); var metaPath = jobInfo.path + Metadata.Ext; File.WriteAllText(metaPath, json); Complete(jobInfo); break; } catch (Exception exception) { error = string.Format("write exception: {0}\n{1}", jobInfo.name, exception); Debug.LogError(error); success = false; } } jobInfo.tried++; if (jobInfo.retry > 0 && jobInfo.tried >= jobInfo.retry) { if (_fileStream != null) { _fileStream.Close(); _fileStream = null; } jobInfo.error = error ?? "unknown error"; Complete(jobInfo); break; } Thread.Sleep(2000); Debug.LogErrorFormat("[retry] download failed: {0}\n{1}", jobInfo.name, error); } }