大きなファイルをOpenStackプロバイダにアップロードする方法を検討中です。 OpenStackは通常、1ファイルにつき5GBの制限がありますが、これはセグメントでファイルをアップロードしてからマニフェストを追加することで回避できます。既に開いているFileStreamのセグメントをStreamパラメータに渡す
はhttp://docs.openstack.org/developer/swift/overview_large_objects.htmlによると、以下のようにこの問題が発生した:
# First, upload the segments
curl -X PUT -H 'X-Auth-Token: <token>' http://<storage_url>/container/myobject/00000001 --data-binary '1'
curl -X PUT -H 'X-Auth-Token: <token>' http://<storage_url>/container/myobject/00000002 --data-binary '2'
curl -X PUT -H 'X-Auth-Token: <token>' http://<storage_url>/container/myobject/00000003 --data-binary '3'
# Next, create the manifest file
curl -X PUT -H 'X-Auth-Token: <token>' -H 'X-Object-Manifest: container/myobject/' http://<storage_url>/container/myobject --data-binary ''
# And now we can download the segments as a single object
curl -H 'X-Auth-Token: <token>' http://<storage_url>/container/myobject
私はC#で最初の部分を作成しようとしているが、私はオープン/クローズ/再開いたFileStreamを防ぎたいです。これは、ファイルの実際のアップロードのためにHttpClient.PutAsyncを使用しているので(そして使用し続けたいと思うので)、問題があります。
using (var client = new HttpClient())
{
client.Timeout = Timeout.InfiniteTimeSpan;
client.DefaultRequestHeaders.Add("X-Auth-Token", "SomeToken");
using (var fs = File.Open(localFilePathAbs, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite))
using (var bs = new System.IO.BufferedStream(fs, 17000000))
{
var response = client.PutAsync(url, new StreamContent(bs), cancellationToken).Result;
return new XauthResponse<string> { Content = Encoding.UTF8.GetString(response.Content.ReadAsByteArrayAsync().Result), StatusCode = response.StatusCode };
}
}
ので、この作業を取得するために、私はHttpClient.PutAsync
にストリームを渡す必要があるまでストリームすでにオープンを読み続ける:このようになりますセグメント化されたアップロードを、必要としないファイルの場合最大量。
私が持っているコードは次のとおりです。
private static void PutSegmented(string url, string localFilePathAbs, long segmentSize, CancellationToken cancellationToken)
{
if (url == null) throw new ArgumentNullException("url");
if (localFilePathAbs ==null) throw new ArgumentNullException("localFilePathAbs");
if (segmentSize == 0) throw new ArgumentNullException("segmentSize");
var fileSize = new FileInfo(localFilePathAbs).Length;
// The number of parts we'll have to upload
var parts = Convert.ToInt64(Math.Ceiling(((double)fileSize)/segmentSize));
// Open the file to upload, use a BufferedStream of roughly 16 megabytes
using (var fs = File.Open(localFilePathAbs, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite))
using (var bs = new System.IO.BufferedStream(fs, 17000000))
{
for (var partIndex = 1; partIndex <= parts; partIndex++)
{
if (cancellationToken.IsCancellationRequested)
break;
// Todo: partIndex has to be prepended with one or more zeros to ensure correct sorting when downloading the object
var segmentUrl = url + "/" + partIndex;
using (var fsSub = new SomeStreamCopyClass(inputStream: bs, maximumToRead: segmentSize))
using (var client = new HttpClient())
{
client.Timeout = Timeout.InfiniteTimeSpan;
client.DefaultRequestHeaders.Add("X-Auth-Token", "SomeToken");
var response = client.PutAsync(segmentUrl, new StreamContent(fsSub), cancellationToken).Result;
// Todo: Use response for verification
}
}
}
// TODO: Upload the manifest
}
だから私が実際に存在する何かをラインusing (var fsSub = new SomeStreamCopyClass(inputStream: bs, maximumToRead: segmentSize))
を置き換えることができれば、私はそれが動作するはずだと思います。
これを使用すると、 'client.PutAsync(segmentUrl、new StreamContent(bs)、cancellationToken).Result'という行はInnerExceptionsの束を持つExceptionをスローします:'要求の送信中にエラーが発生しました。要求は中止されました:要求はキャンセルされました。 - > 'すべてのバイトが書き込まれるまでストリームを閉じることはできません。'おそらく 'PutAsync'は' StreamContent(bs) '内のストリームを閉じますか? – natli
このエラーは、Stream.Lengthを使用してContent-Length httpヘッダーを設定した後、_body_ストリームにデータを書き込もうとしていて、Stream.Lengthよりも少ないデータを読み取っていることがわかります。解決方法は、Stream.Lengthもオーバーライドし、現在のチャンクの長さに設定することです(私の更新された回答を参照)。 – Evk
'Advance'メソッドの' if(ReadTillPosition> base.Length) 'に' ObjectDisposed'例外をスローします。私は本当に 'PutAsync'が' StreamContent'を処分し、 'StreamContent'が基底のストリームを処分すると考えています。だから私は、これが処分を防ぐ方法がない限り、これが継承で機能するとは思わない。 +1あなたの努力のために、これは異なるシナリオで動作するかもしれません – natli