Ich arbeite an einer Methode zum Hochladen großer Dateien an einen OpenStack-Provider. OpenStack hat normalerweise eine Beschränkung von 5 GB pro Datei, aber dies kann umgangen werden, indem die Datei in Segmente hochgeladen und dann ein Manifest hinzugefügt wird.Übergeben eines Segments eines bereits geöffneten FileStream an einen Stream-Parameter
Nach http://docs.openstack.org/developer/swift/overview_large_objects.html geschieht dies wie folgt:
# 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
Ich versuche, den ersten Teil in C# zu erstellen, aber ich möchte das Öffnen/Schließen/Wiedereröffnung des Filestream verhindern. Dies stellt ein Problem dar, weil ich HttpClient.PutAsync für das tatsächliche Hochladen der Datei verwende (und weiterhin verwenden möchte). Für eine Datei, die nicht segmentierten Upload benötigt, sieht das wie folgt aus:
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 };
}
}
Also, um diese Funktion zu erhalten, ich brauche einen Strom HttpClient.PutAsync
zu übergeben, um ein ein bereits geöffnet strom liest weiter Höchstbetrag.
Der Code, den ich habe, ist:
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
}
Also, wenn ich die Linie using (var fsSub = new SomeStreamCopyClass(inputStream: bs, maximumToRead: segmentSize))
mit etwas ersetzen könnte, die tatsächlich vorhanden ist, ich denke, es sollte funktionieren.
Wenn ich dies verwende, löst die Zeile 'client.PutAsync (segmentUrl, neuer StreamContent (bs), cancellationToken) .Result 'eine Exception mit einer Reihe von InnerExceptions aus:' Beim Senden der Anfrage ist ein Fehler aufgetreten -> 'The Anfrage wurde abgebrochen: Die Anfrage wurde abgebrochen. '->' Stream kann nicht geschlossen werden, bis alle Bytes geschrieben sind '- vielleicht schließt 'PutAsync' den Stream innerhalb' StreamContent (bs) '- irgendwelche Ideen? – natli
Dieser Fehler bedeutet, dass es Stream.Length verwendet, um den Content-Length-HTTP-Header festzulegen, dann versucht es, Daten in den Stream _body_ zu schreiben, und entdeckt, dass weniger Daten als Stream.Length gelesen werden. Lösung wäre, auch Stream.Length zu überschreiben und auf die Länge Ihres aktuellen Chunks zu setzen (siehe meine aktualisierte Antwort). – Evk
Es wirft eine 'ObjectDisposed'-Ausnahme bei' if (ReadTillPosition> base.Length) 'innerhalb der' Advance'-Methode. Ich glaube wirklich, dass 'PutAsync' den' StreamContent' entsorgt, der wiederum den zugrunde liegenden Stream entsorgt. Ich denke also nicht, dass dies mit der Vererbung funktionieren wird, es sei denn, es gibt eine Möglichkeit, die Entsorgung zu verhindern. +1 für Ihre Mühe, könnte dies in verschiedenen Szenarien funktionieren – natli