/// <summary> /// 上传数据流 /// </summary> /// <param name="stream">(确定长度的)数据流</param> /// <param name="key">要保存的key</param> /// <param name="token">上传凭证</param> /// <param name="putExtra">上传可选设置</param> /// <returns>上传数据流后的返回结果</returns> public async Task <HttpResult> UploadStream(Stream stream, string key, string token, PutExtra putExtra) { if (putExtra == null) { putExtra = new PutExtra(); } if (string.IsNullOrEmpty(putExtra.MimeType)) { putExtra.MimeType = ContentType.APPLICATION_OCTET_STREAM; } if (putExtra.ProgressHandler == null) { putExtra.ProgressHandler = DefaultUploadProgressHandler; } if (putExtra.UploadController == null) { putExtra.UploadController = DefaultUploadController; } var fileName = key; if (string.IsNullOrEmpty(key)) { fileName = "fname_temp"; } var result = new HttpResult(); try { var boundary = HttpManager.CreateFormDataBoundary(); var content = new MultipartFormDataContent(boundary); var length = stream.Length; putExtra.ProgressHandler(0, length); // Key if (!string.IsNullOrEmpty(key)) { content.Add(new StringContent(key), "key"); } // Token content.Add(new StringContent(token), "token"); // Other params if (putExtra.Params != null) { foreach (var param in putExtra.Params) { content.Add(new StringContent(param.Value), param.Key); } } // Reuse stream if (!stream.CanSeek) { var ms = new MemoryStream((int)stream.Length); stream.CopyTo(ms); stream.Dispose(); stream = ms; } // CRC32 var crc32 = Crc32.CheckSumStream(stream); stream.Seek(0, SeekOrigin.Begin); content.Add(new StringContent(crc32.ToString()), "crc32"); // Primary content var part = new StreamContent(stream); part.Headers.ContentType = MediaTypeHeaderValue.Parse(putExtra.MimeType); content.Add(part, "file", fileName); // Get upload host var ak = UpToken.GetAccessKeyFromUpToken(token); var bucket = UpToken.GetBucketFromUpToken(token); if (ak == null || bucket == null) { return(HttpResult.InvalidToken); } var uploadHost = await _config.UpHost(ak, bucket); // TODO: Real progress putExtra.ProgressHandler(length / 5, length); result = await _httpManager.PostAsync(uploadHost, content, boundary); putExtra.ProgressHandler(length, length); if (result.Code == (int)HttpCode.OK) { result.RefText += $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}] [FormUpload] Uploaded: #STREAM# ==> \"{key}\"\n"; } else { result.RefText += $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}] [FormUpload] Failed: code = {result.Code}, text = {result.Text}\n"; } } catch (Exception ex) { var sb = new StringBuilder(); sb.Append($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}] [FormUpload] Error: "); var e = ex; while (e != null) { sb.Append(e.Message + " "); e = e.InnerException; } sb.AppendLine(); if (ex is QiniuException qex) { result.Code = qex.HttpResult.Code; result.RefCode = qex.HttpResult.Code; result.Text = qex.HttpResult.Text; result.RefText += sb.ToString(); } else { result.RefCode = (int)HttpCode.USER_UNDEF; result.RefText += sb.ToString(); } } return(result); }