问题
For some reason my below code that used to work now consequently raises an exception:
public static async Task<string> HttpPut(string inUrl, string inFilePath)
{
using (var handler = new HttpClientHandler
{
AllowAutoRedirect = false
})
{
using (var client = new HttpClient(handler))
{
//var content = new StreamContent(new FileStream(inFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true));
using (var content = new StreamContent(new FileStream(inFilePath, FileMode.Open)))
{
content.Headers.Remove("Content-Type");
content.Headers.Add("Content-Type", "application/octet-stream");
using (var req = new HttpRequestMessage(HttpMethod.Put, inUrl))
{
string authInfo = String.Format("{0}:{1}", Program.Config.MediaStorageList.Find(o => o.Name == "Viz Media Engine Test").UserName, Program.Config.MediaStorageList.Find(o => o.Name == "Viz Media Engine Test").Password);
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
req.Headers.Add("Authorization", "Basic " + authInfo);
req.Headers.Remove("Expect");
req.Headers.Add("Expect", "");
//req.Headers.TransferEncodingChunked = true;
req.Content = content;
// Ignore Certificate validation failures (aka untrusted certificate + certificate chains)
ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
using (HttpResponseMessage resp = await client.SendAsync(req))
{
//This part is specific to the setup on an Expo we're at...
if (resp.StatusCode == HttpStatusCode.Redirect || resp.StatusCode == HttpStatusCode.TemporaryRedirect)
{
string redirectUrl = resp.Headers.Location.ToString();
if (redirectUrl.Contains("vme-store"))
{
redirectUrl = redirectUrl.Replace("vme-store", "10.230.0.11");
}
return await HttpPut(redirectUrl, inFilePath);
}
resp.EnsureSuccessStatusCode();
return await resp.Content.ReadAsStringAsync();
}
}
}
}
}
}
The exception I'm getting is:
System.NotSupportedException was unhandled
HResult=-2146233067
Message=The stream does not support concurrent IO read or write operations.
Source=System
StackTrace:
at System.Net.ConnectStream.InternalWrite(Boolean async, Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
at System.Net.ConnectStream.BeginWrite(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
at System.Net.Http.StreamToStreamCopy.BufferReadCallback(IAsyncResult ar)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at VizWolfInnerServer.Tools.HttpConnector.<HttpPut>d__39.MoveNext() in c:\Users\christer\Documents\Visual Studio 2012\Projects\VizWolfNew\VizWolfInnerServer\Tools\HttpConnector.cs:line 202
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at VizWolfInnerServer.Tools.VizAPIConnector.<VmeUploadMedia>d__0.MoveNext() in c:\Users\christer\Documents\Visual Studio 2012\Projects\VizWolfNew\VizWolfInnerServer\Tools\VizAPIConnector.cs:line 187
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>b__1(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
InnerException:
I'm having a very hard time finding proper documentation and examples for HttpClient, and I'm struggling with figuring out why this suddenly doesn't work (a totally similar method with StringContent instead of StreamContent works perfectly)...
It originally gets called from it's own thread, and then like this:
public static async void VmeUploadMedia(string inUploadLink, string inFilePath)
{
string result = await HttpConnector.HttpPut(inUploadLink, inFilePath);
}
Anyone spot anything obvious?
Thanks
UPDATE
Turns out getting the expo-guys to map their storage-name with it's IP so I could go back to my original code was the best solution. The problem I was having is something to do with AllowAutoRedirect = false. The exception occured on HttpResponseMessage resp = await client.SendAsync(req), even if there was no redirection really going on. I'm kind of lost as to why it was even happening, but using this code everything is working now:
public static async Task<string> HttpPut(string inUrl, string inFilePath)
{
using (var client = new HttpClient())
{
using (var content = new StreamContent(File.OpenRead(inFilePath)))
{
content.Headers.Remove("Content-Type");
content.Headers.Add("Content-Type", "application/octet-stream");
using (var req = new HttpRequestMessage(HttpMethod.Put, inUrl))
{
string authInfo = String.Format("{0}:{1}", Program.Config.MediaStorageList.Find(o => o.Name == "Viz Media Engine Test").UserName, Program.Config.MediaStorageList.Find(o => o.Name == "Viz Media Engine Test").Password);
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
req.Headers.Add("Authorization", "Basic " + authInfo);
req.Headers.Remove("Expect");
req.Headers.Add("Expect", "");
req.Content = content;
// Ignore Certificate validation failures (aka untrusted certificate + certificate chains)
ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
using (HttpResponseMessage resp = await client.SendAsync(req))
{
resp.EnsureSuccessStatusCode();
return await resp.Content.ReadAsStringAsync();
}
}
}
}
}
Thanks to the people who were trying to help
回答1:
Turns out getting the expo-guys to map their storage-name with it's IP so I could go back to my original code was the best solution. The problem I was having is something to do with AllowAutoRedirect = false. The exception occured on HttpResponseMessage resp = await client.SendAsync(req), even if there was no redirection really going on. I'm kind of lost as to why it was even happening, but using this code everything is working now:
public static async Task<string> HttpPut(string inUrl, string inFilePath)
{
using (var client = new HttpClient())
{
using (var content = new StreamContent(File.OpenRead(inFilePath)))
{
content.Headers.Remove("Content-Type");
content.Headers.Add("Content-Type", "application/octet-stream");
using (var req = new HttpRequestMessage(HttpMethod.Put, inUrl))
{
string authInfo = String.Format("{0}:{1}", Program.Config.MediaStorageList.Find(o => o.Name == "Viz Media Engine Test").UserName, Program.Config.MediaStorageList.Find(o => o.Name == "Viz Media Engine Test").Password);
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
req.Headers.Add("Authorization", "Basic " + authInfo);
req.Headers.Remove("Expect");
req.Headers.Add("Expect", "");
req.Content = content;
// Ignore Certificate validation failures (aka untrusted certificate + certificate chains)
ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
using (HttpResponseMessage resp = await client.SendAsync(req))
{
resp.EnsureSuccessStatusCode();
return await resp.Content.ReadAsStringAsync();
}
}
}
}
}
Thanks to the people who were trying to help
回答2:
using (HttpResponseMessage resp = await client.SendAsync(req))
It seems that this line will create a new context for that execution, hence a new thread context, maybe you cannot do that, because you will share the lock of the FileStream.
回答3:
It looks like you are calling HttpPut()
again, but you still have the FileStream
opened. Try disposing of the FileStream before you recursively call HttpPut()
from within itself.
//This part is specific to the setup on an Expo we're at...
if (resp.StatusCode == HttpStatusCode.Redirect || resp.StatusCode == HttpStatusCode.TemporaryRedirect)
{
string redirectUrl = resp.Headers.Location.ToString();
if (redirectUrl.Contains("vme-store"))
{
redirectUrl = redirectUrl.Replace("vme-store", "10.230.0.11");
}
content.Dispose();
return await HttpPut(redirectUrl, inFilePath);
}
In addition, you probably would want to dispose of any other IDisposable objects like the http response to ensure that all resources are freed before you proceed deeper into the stack trace. This is one problem with recursion, that while you are using Using
statements you not leaving their scope so they are not doing their job.
来源:https://stackoverflow.com/questions/15885990/c-sharp-httpclient-put