Posting MultipartFormDataContent request with HttpClient gets Error while copying content to a stream - asp.net-core

I send HTTP request with the MultipartFormDataContent but I'm getting the following error when the file size is more than 2mb:
Error while copying content to a stream.
Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host..
An existing connection was forcibly closed by the remote host.
But exactly the same request with more than 5mb file can be sent with PostMan successfully.
This is my code:
using (var client = new HttpClient(GetHttpClientHandler(requestUrl)))
{
using (var content = new MultipartFormDataContent())
{
for (int i = 0; i < images.Count; i++)
{
var fileName = $"file {(i + 1)}.png";
ByteArrayContent bContent = new ByteArrayContent(images[i]);
content.Add(bContent, "file", fileName);
}
using (var response = await client.PostAsync(requestUrl, content)) //Exception occurs here
{
resp = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
}
}
}
So, if the problem about the server-side why it works with PostMan? If the problem is in my code, what is that then? I think I did everything by convention.

This is not the recommended way of using HttpClient!
First create custom client servis with the required upload method:
public class MyClientService : IMyClientService
{
private readonly HttpCLient _client;
public MyClientService(HttpClient client)
{
_client = client;
}
public async Task<bool> UploadFilesAsync(MultipartFormDataContent content, string requestUrl)
{
var response = await _client.PostAsync(requestUrl, content);
// ...
}
}
Then register the custom client service in startup:
services.AddHttpClient<IMyClientService, MyClientService>();
Then inject your service to the controller/page model:
private readonly IMyClientService_client;
public UploadPage(IMyClientService client)
{
_client = client;
}
And use it to upload files:
using (var content = new MultipartFormDataContent())
{
for (int i = 0; i < images.Count; i++)
{
var fileName = $"file {(i + 1)}.png";
ByteArrayContent bContent = new ByteArrayContent(images[i]);
content.Add(bContent, "file", fileName);
}
var success = await _client.UploadFilesAsync(content, requestUrl);
}

Related

C# How I can upload file to MinIO (AWS S3 compatible API) via gRPC without buffering data?

How can I upload large files to MinIO (AWS S3 compatible API) via gRPC service without buffering data?
I have gRPC service with following definition:
service MediaService {
rpc UploadMedia(stream UploadMediaRequest) returns (UploadMediaResponse);
}
message UploadMediaRequest {
oneof Data {
UploadMediaMetadata metadata = 1;
UploadMediaStream fileStream = 2;
}
}
message UploadMediaMetadata {
string bucket = 1;
string virtialDirectory = 2;
string fileName = 3;
string contentType = 4;
map<string, string> attributes = 6;
}
message UploadMediaStream {
bytes bytes = 1;
}
And implementation of UploadMedia:
public override async Task<UploadMediaResponse> UploadMedia(
IAsyncStreamReader<UploadMediaRequest> requestStream,
ServerCallContext context)
{
UploadMediaMetadata? metadata = null;
var token = context.CancellationToken;
var traceId = context.GetHttpContext().TraceIdentifier;
await using var memoryStream = new MemoryStream();
await foreach (var req in requestStream.ReadAllAsync(token))
{
if (req.DataCase == UploadMediaRequest.DataOneofCase.Metadata)
{
metadata = req.Metadata;
_logger.LogTrace("[Req: {TraceId}] Received metadata", traceId);
}
else
{
await memoryStream.WriteAsync(req.FileStream.Bytes.Memory, token);
_logger.LogTrace("[Req: {TraceId}] Received chunk of bytes", traceId);
}
}
if (metadata == null)
{
throw new RpcException(new Status(StatusCode.InvalidArgument, "Not found metadata."));
}
memoryStream.Seek(0L, SeekOrigin.Begin);
var uploadModel = _mapper.Map<UploadModel>(metadata);
uploadModel.FileStream = memoryStream;
var file = await _fileService.UploadFile(uploadModel, token);
await _eventsService.Notify(new MediaUploadedEvent(file.PublicId), token);
_logger.LogTrace("[Req: {TraceId}] File uploaded", traceId);
return new UploadMediaResponse { File = _mapper.Map<RpcFileModel>(file) };
}
At the method I read request stream and write data to MemoryStream. After that I upload file to storage:
var putObjectArgs = new PutObjectArgs()
.WithStreamData(fileStream)
.WithObjectSize(fileStream.Length)
.WithObject(virtualPath)
.WithBucket(bucket)
.WithContentType(contentType)
.WithHeaders(attributes);
return _storage.PutObjectAsync(putObjectArgs, token);
I want to upload files without buffering data in Memory.
I think I can write bytes from stream to disk and after that create FileStream, but I don't want one more dependency.

PostAsync request with Array parameter on MVC Web API

I have Xamarin application that has POST request with array list of parameter and on my MVC WEB API we used code first Entity framework. Both was separated project solutions (.sln).
On my Xamarin project, I have PostAsync request which supplies List of array values.
using (var client = new HttpClient())
{
Parameter = string.Format("type={0}&param={1}",type, param[]);
var data = JsonConvert.SerializeObject(parameters);
var content = new StringContent(data, Encoding.UTF8, "application/json");
using (var response = await client.PostAsync(url, content))
{
using (var responseContent = response.Content)
{
result = await responseContent.ReadAsStringAsync();
}
}
}
Then In my Web API controller I have same parameter with my client side also.
[System.Web.Http.AcceptVerbs("GET", "POST")]
[System.Web.Http.HttpPost]
[Route("type={type}&param={param}")]
public BasicResponse applog([FromUri] ProfilingType type , List<string> param)
{
if (ModelState.IsValid == false)
{
throw new ModelValidationException("Model state is invalid.");
}
try
{
if(type == ProfilingType.Login)
{
var command = new SendDataProfilingCommand(param);
CommandHandler.Execute(command);
}
else
{
var command = new UpdateDataProfilingCommand(type,param);
CommandHandler.Execute(command);
}
}
catch (Exception e)
{
throw new Exception(e.Message);
}
return new BasicResponse
{
Status = true,
Message = Ok().ToString()
};
}
Since I'm not with the API, I want to test it first on Postman or even in the URL. but my problem was when i Try to test it using this url below
http://localhost:59828/api/users/applog?type=1&param=[1,Caloocan,Metro Manila,Philippines,0,0]
I received this message : No HTTP resource was found that matches the request URI ......
My Question is, How can I test my Web API with List Parameter on URL or in the Postman ? and What Format I can use when sending a post request into my Xamarin PostAsync request?
You don't need to send as Content.
using (var client = new HttpClient())
{
Parameter = string.Format("type={0}&param={1}",type, param[]);
url = url + "?" + Parameter;
using (var response = await client.PostAsync(url))
{
using (var responseContent = response.Content)
{
result = await responseContent.ReadAsStringAsync();
}
}
}

Saving data in windows phone received from WCF/web service .

Saving data in windows phone received from WCF/web service .
The response may be received after sometime so how to handle this situation.
Saving data is no problem but How to handel if data is received late
You can use this code (show the code from my project):
public void sendPost(string postData, Action<MyResponse, Exception> callback, CreateResponse creater)
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(UrlRequest);
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Accept = "application/json";
webRequest.AllowAutoRedirect = true;
webRequest.BeginGetRequestStream(new AsyncCallback(getRequestStreamCallback), new Request()
{
HttpRequest = webRequest,
PostData = postData,
Url = UrlRequest,
CallBack = callback,
Creater = creater
});
}
private void getRequestStreamCallback(IAsyncResult asynchronousResult)
{
var request = (Request)asynchronousResult.AsyncState;
// End the stream request operation
Stream postStream = request.HttpRequest.EndGetRequestStream(asynchronousResult);
byte[] byteArray = Encoding.UTF8.GetBytes(request.PostData);
// Add the post data to the web request
postStream.Write(byteArray, 0, byteArray.Length);
postStream.Close();
// Start the web request
request.HttpRequest.BeginGetResponse(new AsyncCallback(getResponseCallback), request);
}
private void getResponseCallback(IAsyncResult asynchronousResult)
{
var request = (Request)asynchronousResult.AsyncState;
try
{
HttpWebResponse response;
// End the get response operation
response = (HttpWebResponse)request.HttpRequest.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamReader = new StreamReader(streamResponse);
var myResponse = streamReader.ReadToEnd();
streamResponse.Close();
streamReader.Close();
response.Close();
MyResponse response_obj = request.Creater.CreateResponseObj();
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(myResponse)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(response_obj.GetType());
response_obj = (GYResponse)serializer.ReadObject(stream);
if (request.CallBack != null)
{
request.CallBack.Invoke(response_obj, null);
}
}
}
catch (WebException e)
{
if (request.CallBack != null)
{
request.CallBack.Invoke(null, e);
}
}
}
public void getInfo(string uid, Action<MyResponse, Exception> callback)
{
CreateResponse creater = new CreateResponseGetInfo();
string model = "User";
string method = "getInfo";
Params parametrs = new Params();
parametrs.Uid = uid;
//create yor request
string request = getRequestString(model, method, parametrs, Atoken);
sendPost(request, callback, creater);
}
So, you call method, which send request to web service postRequester.getInfo(uid, ResponseHandler) and use delegate for processing result.
private void ResponseHandler(MyResponse result, Exception error)
{
if (error != null)
{
string err = error.Message;
return;
}
else
{
var infoResponse = result as ResponseGetInfo;
if (infoResponse != null)
{
//result processing..
}
}
}
All the web requests you make in a Windows Phone app are Asynchronous. That means, you make a web request from your app and attach a handler to handle the response when it comes. In the response handler, you will have to take care of the response and do whatever you want with it.
Check this link Using WebClient and HttpWebRequest

Create Registrant using GoToWebinar

I want to create a registrant for a webinar using GoToWebinar API's. I came across the code at gotowebinar api php
I provided my username and password to get the oAuth object. This worked perfectly fine as described.
Now I want to do something like this:
I have a Registration page. When user fills in the required details, selects the 'register to webinar' option and clicks on 'Submit', I want to enrol him for that webinar using CreateRegistrant API. The problem is, I am not able to get the oAuth object without providing username and password. Is there a way to pass this programatically and create oAuth object?
I store my API key, UserID and password in my WebConfig then read them into a Login Object for use when I do authorization. Here's how I do it in C#:
public class Login
{
public string UserId
{ get { return System.Configuration.ConfigurationManager.AppSettings["GTWUserId"]; } }
public string Password
{ get { return System.Configuration.ConfigurationManager.AppSettings["GTWPassword"]; } }
public string APIKey
{ get { return System.Configuration.ConfigurationManager.AppSettings["GTWAPIKey"]; } }
}
public string DoAuthorize()
{
Login lg = new Login();
string sError = "";
// first we need to create the uri for the web request
string uri = String.Format("https://api.citrixonline.com/oauth/access_token?grant_type=password&user_id={0}&password={1}&client_id={2}",
lg.UserId, lg.Password, lg.APIKey);
// then the request to login is created and sent. From the response
// we need to store at least the access token and the organizer key
// to use for further calls
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Accept = "application/json";
request.ContentType = "application/json";
try
{
var response = request.GetResponse();
//the following lines duplicate the response stream so we can read it for
//deserialization and also re-read it and write it out.
using (MemoryStream ms = new MemoryStream())
{
var stream = response.GetResponseStream();
stream.CopyTo(ms);
ms.Position = 0;
stream.Close();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ResponseDirectLogin));
var deserialized = (ResponseDirectLogin)ser.ReadObject(ms);
auth.OauthToken = deserialized.AccessToken;
auth.OrganizerKey = deserialized.OrganizerKey;
}
}
catch (WebException e)
{
using (var sr = new StreamReader(e.Response.GetResponseStream()))
sError = sr.ReadToEnd();
sError = String.Concat(sError, "/n", uri);
}
return sError;
}
public class Auth {
public string OauthToken { get; set; }
public string OrganizerKey { get; set; }
}
public static Auth auth = new Auth(); // This is actually in a BaseControlelr inherited by our MVC Home Controller.
public string DoRegister(string WebinarKey)
{
// Here we authorize if we haven't alerady
if (auth.OauthToken == null)
{
sMessage = DoAuthorize();
}
// first we need to create the uri for the web request
// OrganizerKey is your authorization key for the webinar organizer
string uri = String.Format(#"https://api.citrixonline.com/G2W/rest/organizers/{0}/webinars/{1}/registrants",
OrganizerKey, WebinarKey);
//then create and serialize the registrant object
// This is for when you have questions on your webinar, you can omit them if you don't have any
List<questions> q = GetQuestionKeys(Key, OrganizerKey);
List<response> responses_ = new List<response>();
foreach (var question in q)
{
response res1 = new response();
res1.questionKey = question.questionKey;
// determine which question and set the response
if (question.question == "question")
{
res1.responseText = "response";
responses_.Add(res1);
}
}
var registrant = new Registrant
{
firstName = FirstName,
lastName = LastName,
email = EmailAddress,
responses = responses_.ToArray()
};
JavaScriptSerializer ser = new JavaScriptSerializer();
string json = ser.Serialize(registrant);
// then the request to create a registrant is created and sent
// N.B. we need to include the access token to the headers to access
// the user's account and data
try {
WebClient client = new WebClient();
client.Headers = new WebHeaderCollection();
client.Headers.Add("Accept", "application/vnd.citrix.g2wapi-v1.1+json");
client.Headers.Add("Content-type", "application/json");
client.Headers.Add("Authorization", string.Format("OAuth oauth_token={0}", OAuthToken));
try
{
string resp = client.UploadString(uri, "POST", json);
var ok = ser.Deserialize<ResponseCreateRegistrantOk>(resp);
}
catch (WebException e)
{
//if there is an error, e.g. the registrant exists already
// we need an alternative deserialization
Stream s = new MemoryStream();
using (Stream response = e.Response.GetResponseStream())
{
byte[] buffer = new byte[1024];
int byteCount;
do
{
byteCount = response.Read(buffer, 0, buffer.Length);
s.Write(buffer, 0, byteCount);
} while (byteCount > 0);
}
s.Seek(0, SeekOrigin.Begin);
string content = new StreamReader(s, Encoding.UTF8).ReadToEnd();
s.Seek(0, SeekOrigin.Begin);
using (var err = new StreamReader(s))
{
var sb = new StringBuilder("Registration Error\n");
if (content.IndexOf("int_err_code") > -1)
{
var dupe = ser.Deserialize<ResponseCreateRegistrantDuplicate>(err.ReadToEnd());
sb.AppendFormat(String.Format("Error Code: {0}<br />", dupe.ErrorCode));
sb.AppendFormat(String.Format("Message: {0}<br />", dupe.Message));
}
else
{
var dupe = ser.Deserialize<ResponseCreateRegistrantDuplicate>(err.ReadToEnd());
sb.AppendFormat(String.Format("Description: {0}<br />", dupe.Description));
//sb.AppendFormat(String.Format("Incident: {0}<br />", dupe.Incident));
//sb.AppendFormat(String.Format("Registrant key: {0}<br />", dupe.RegistrantKey));
sb.AppendFormat(String.Format("Join Url: {0}<br />", dupe.JoinUrl));
}
sMessage = sb.ToString();
}
}
} catch (Exception exc) {
exc.Data.Add("stringInfo", "inside");
return "";
}
return sMessage;
}

ProtocolError while calling HttpWebRequest.GetResponse()

I have a page containing links to some files.
I basically need to access the source of the page for parsing it then and obtaining all the hyperlinks to the files.
My code is something like this (some piece of code I've found in many places on the net ..):
"private static byte[] ReadImageFromUrl(string url)
{
var myReq = (HttpWebRequest)WebRequest.Create(url);
myReq.Timeout = 10000;
WebResponse myResp = myReq.GetResponse();
Stream stream = myResp.GetResponseStream();
List<byte> bytesList = new List<byte>();
using (var br = new BinaryReader(stream))
{
try
{
while (true)
{
var b = br.ReadByte();
bytesList.Add(b);
}
}
catch (Exception)
{}
br.Close();
}
myResp.Close();
return bytesList.ToArray();
}"
Now the problem is I get "System.Net.WebException: The remote server returned an error: (500) Internal Server Error." when calling "myReq.GetResponse()" - examining the error I see that the status is 'ProtocolError'.
The response property of the WebException object contains some server error ..(although when opening it from the browser it opens correctly) ...also when I call this function with the url of one of my files I get the same ProtocolError status, but the 404 error ...
Please give any hint how could I solve it... or any other possibility of accomplishing this task.
Thanks !
My new code after using Fiddler is:
private static byte[] ReadFileFromUrl(string url)
{
var myReq = (HttpWebRequest)WebRequest.Create(url);
myReq.Accept = const_AcceptHeader;
myReq.Headers.Set(const_AcceptLanguageHeaderName, const_AcceptLanguageHeader);
myReq.UserAgent = const_AcceptUserAgentHeader;
myReq.CookieContainer = new CookieContainer();
myReq.KeepAlive = true;
myReq.Timeout = Int32.Parse(ConfigSettings.RequestPageTimeout) * 1000;
WebResponse myResp = null;
List<byte> bytesList = null;
myResp = myReq.GetResponse();
Stream stream = myResp.GetResponseStream();
bytesList = new List<byte>();
using (var br = new BinaryReader(stream))
{
try
{
while (true)
{
var b = br.ReadByte();
bytesList.Add(b);
}
}
catch (Exception ex)
{
throw;
}
br.Close();
}
return bytesList.ToArray();
}
All variables that start with const_ are taken from Fiddler.
Well, I solved that using Fiddler ... I passed to my request object the headers as I have seen them in Fiddler ...& it worked, no error