I am calling a JVA RESTful API from my c# code and passing a data type "MultipartFormDataContent" as one of the parameters.
This has an object named "attachments" and the size of it could be as big as 2GB.
All this works fine when I run the code locally from my PC.
However, when I host the code on my IIS app server it fails when the file size is >20MB. It works fine for files <20MB.
In IIS, I do have the "RequestFiltering maxAllowedContentLength set to 2147483647".
Here is the code:
HttpContent messageContent = new StringContent(jss.Serialize(new
{
to ="abc#xyz.com"
cc ="abc#xyz.com",
subject = "Subject",
body ="body"
}));
using (var client = new HttpClient())
{
client.Timeout = new TimeSpan(12, 0, 0);
var section = System.Configuration.ConfigurationManager.GetSection("secureAppSettings") as NameValueCollection;
byte[] credentials = Encoding.UTF8.GetBytes(section["username"] + ":" + section["password"]);
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(credentials));
using (var formData = new MultipartFormDataContent())
{
formData.Add(messageContent, "message");
List<Tuple<string, string, long>> listOfFiles = new List<Tuple<string, string, long>>();
if (Directory.Exists(HttpContext.Current.Server.MapPath(System.Configuration.ConfigurationManager.AppSettings["RootDocumentTemplateFileRecreatePath"] + instanceID)))
{
string[] filePaths = Directory.GetFiles(HttpContext.Current.Server.MapPath(System.Configuration.ConfigurationManager.AppSettings["RootDocumentTemplateFileRecreatePath"] + instanceID));
// listOfFiles.Add(new Tuple<string, string, long>(Path.GetFileName(filePaths[0]), filePaths[0], new FileInfo(filePaths[0]).Length));
foreach (var eachFilePath in filePaths)
{
string filename = Path.GetFileName(eachFilePath);
string filePath = eachFilePath;
long fileSize = new FileInfo(eachFilePath).Length;
var stream = new FileStream(eachFilePath,
FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
{
client.Timeout = new TimeSpan(12, 0, 0);
var fileContent = new StreamContent(stream, 1024 * 1024 * 100);
{
var size = fileSize;
fileContent.Headers.ContentDisposition = new
ContentDispositionHeaderValue("attachments")
{
FileName = filename,
DispositionType = DispositionTypeNames.Attachment,
Name = "attachments"
};
fileContent.Headers.ContentType = new MediaTypeHeaderValue("multipart/form-data");
fileSizes.Add(filename, size);
formData.Add(fileContent, "attachments", filename);
}
}
}
fileSizesContent = new StringContent(jss.Serialize(fileSizes));
formData.Add(fileSizesContent, "fileSizes");
}
var response = client.PostAsync(<java RESTful service url>,formData).Result;
Here is the response object I get on sending files >20MB:
adRequestMethod: POST, RequestUri: 'RESTful service url', Version: 1.1, Content: System.Net.Http.MultipartFormDataContent, Headers:
{
Authorization: Basic TWVzc2FnZUNvdXJpZXJVc2VyOlN0YXJzTWVzc2FnZUNvdXJpZXIwMQ==
Content-Type: multipart/form-data; boundary="08c7381e-a190-4c90-98b3-a0a1dae5107b"
Content-Length: 19998604
}Bad RequestPragma: no-cache
Connection: close
Cache-Control: no-store, must-revalidate, no-cache
Date: Thu, 30 Mar 2017 17:40:02 GMT
Appreciate your help.
Thanks in advance.
Regards,
Sridhar
Related
So, I have a POST request to upload a file. I'm able to do that request inside a postman with simple settings like this:
Body: form-data, body has only one item. Key=file, Value=xxxx.pdf
No authorization. Final working request from postman console looks like this:
Request Headers
User-Agent: PostmanRuntime/7.26.8
Accept: */*
Cache-Control: no-cache
Postman-Token: 8d5df709-8f9e-48e2-bf20-f300b24d4be8
Host: api.xxxx.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------------------------138420394858496796018969
Cookie: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Length: 976797
Request Body
file: undefined
This works and file gets uploaded. But when I do the same thing in .net Core, it fails every time (400 - bad request)
using (var message = new HttpRequestMessage(HttpMethod.Post, uploadUri))
{
using (var fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read))
using (var formDataContent = new MultipartFormDataContent())
{
using (var fileContent = new StreamContent(fileStream))
{
formDataContent.Add(fileContent, "file");
message.Content = formDataContent;
using (var response = await httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead))
{
response.EnsureSuccessStatusCode();
}
}
}
}
Edit: Only difference that I can see is the content-disposition header being added to StreamContent. Hovewer I was not able to remove this header.
Edit: After a talk with the developer of the API, the problem is 100% the body & headers of the request. Api does not want a content-disposition header and excpects body multipart/form-data with pair file=byteArray
Might be security issue, try to add this before HTTP request code
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
I do believe you're missing Content-Type header value set:
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
I've tested with a *.jpg file and it works OK:
using (var multipartFormContent = new MultipartFormDataContent())
{
//Load the file and set the file's Content-Type header
var fileStreamContent = new StreamContent(File.OpenRead(filePath));
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
//Add the file to form
multipartFormContent.Add(fileStreamContent, name: "file", fileName: filePath);
//Send it
var response = await new HttpClient().PostAsync("http://localhost:5000/[Controller]/[Action]", multipartFormContent);
response.EnsureSuccessStatusCode();
}
So in the end. It was Oliver from comments who pointed me in a right direction and with Fiddler I found the solution. The problem was in a content-type header. When this header is generated in postman, boundary is without quotation marks. Httpclient hovewer adds those automaticaly. These were the reason why my request was being rejected.
Working code:
using (var message = new HttpRequestMessage(HttpMethod.Post, uploadUri))
{
var boundary = $"--------------------------{Guid.NewGuid().ToString("N").ToUpper()}";
using (var fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read))
using (var content = new MultipartFormDataContent(boundary))
{
content.Headers.Remove("Content-Type");
content.Headers.TryAddWithoutValidation("Content-Type", $"multipart/form-data; boundary={boundary}");
using (var fileContent = new StreamContent(fileStream))
{
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
fileContent.Headers.ContentDisposition.Name = "\"file\"";
fileContent.Headers.ContentDisposition.FileName = $"\"{fileInfo.Name}\"";
fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
content.Add(fileContent);
message.Content = content;
using (var response = await httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead))
{
.....
}
}
}
}
I'm encountering an issue uploading a file (image for this example) along with a JSON string. I am as the client, trying to upload to a webserver. Currently on the website itself, when uploading a file, the payload is this:
------WebKitFormBoundary1A4Toq4hnrayCRu4
Content-Disposition: form-data; name="FileInstance"
{"action":["/public/my_folder"],"fileName":"download_icon.png","fileSize":313,"certification":{"level":"0","Groups":[]}}
------WebKitFormBoundary1A4Toq4hnrayCRu4
Content-Disposition: form-data; name="download_icon.png"; filename="download_icon.png"
Content-Type: image/png
------WebKitFormBoundary1A4Toq4hnrayCRu4--
I am POSTing by 2 separate requests, and each result has a 200 status code, but looking into the result, it's empty and I should be receiving an md5hash of the file uploaded but I am not.
Here is my code:
// Uploading the JSON first
MultipartFormDataContent form = new MultipartFormDataContent();
var boundary = $"----WebKitFormBoundary" + DateTime.Now.Ticks.ToString("x");
form.Headers.Remove("Content-Type");
form.Headers.Add("Content-Type", $"multipart/form-data; boundary={boundary}");
MyFileClass currentFile = new MyFileClass();
currentFile.action = new List<string>() { "/public/my_folder" };
currentFile.filename = Path.GetFileName(Filename);
currentFile.fileSize = Convert.ToInt32(new FileInfo(Filename).Length);
currentFile.certification= new MyFileClass.Certification();
CreateCertification(currentFile.certification);
var json = JsonConvert.SerializeObject(currentFile);
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = Path.GetFileName(Filename),
FileName = Path.GetFileName(Filename)
};
var response = await myHttpClient.PostAsync(url, form);
var result_str = response.Content.ReadAsStringAsync().Result;
// Uploading the actual file
var stream = new FileStream(Filename, FileMode.Open);
form = new MultipartFormDataContent();
boundary = $"----WebKitFormBoundary" + DateTime.Now.Ticks.ToString("x");
form.Headers.Remove("Content-Type");
form.Headers.Add("Content-Type", $"multipart/form-data; boundary={boundary}");
content = new StreamContent(stream);
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "FileInstance"
};
content = new StreamContent(stream);
form.Add(content, Path.GetFileNameWithoutExtension(Filename));
response = await myHttpClient.PostAsync(url, form);
result_str = response.Content.ReadAsStringAsync().Result;
Edit 1: This is how httpclient is defined:
string password = SecureStringExtensions.ToUnsecuredString(Password);
var credCache = new CredentialCache
{
{
new Uri(url), "Basic", new NetworkCredential(Username, password)
}
};
var myHttpClient = new HttpClient(new HttpClientHandler() { Credentials = credCache });
myHttpClient.DefaultRequestHeaders.Add("Connection", "keep-alive");
myHttpClient.Timeout = Timeout.InfiniteTimeSpan;
I was trying different methods of POSTing which didn't work. Now I found a solution, where I removed editing the boundary and ContentType, and straight forward added the file and JSON at the same time to MultipartFormDataContent and it worked fine.
MyFileClass currentFile = new MyFileClass();
currentFile.action = new List<string>() { "/public/my_folder" };
currentFile.filename = Path.GetFileName(Filename);
currentFile.fileSize = Convert.ToInt32(new FileInfo(Filename).Length);
currentFile.certification= new MyFileClass.Certification();
CreateCertification(currentFile.certification);
var json = JsonConvert.SerializeObject(currentFile);
var formContent = new MultipartFormDataContent
{
{ new StringContent(json, Encoding.UTF8, "application/json") },
{ new StreamContent(new MemoryStream(File.ReadAllBytes(Filename))), Path.GetFileName(Filename) ,Path.GetFileName(Filename)}
};
var response = await myHttpClient.PostAsync(url, formContent);
string stringContent = await response.Content.ReadAsStringAsync();
Where Filename is the absolute path of the file itself.
I'm trying to POST a Document (any file type) to a GLPI server through API REST.
Here is what I'm doing:
private void button11_Click(object sender, EventArgs e)
{
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
var rcontent = string.Empty;
// HEADERS (URL + Access Tokens)
//string _ContentType = "multipart/form-data";
string _Uri = Properties.Settings.Default.GLPI_URL + "/Document/";
client.BaseAddress = new Uri(_Uri);
//client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(_ContentType));
client.DefaultRequestHeaders.Add("Session-Token", Properties.Settings.Default.GLPI_SESSION_TOKEN);
client.DefaultRequestHeaders.Add("App-Token", Properties.Settings.Default.GLPI_APP_TOKEN);
// JSON Content (input string array with file uploaded informations)
JSON_C.DocumentAdder JSONContent = new JSON_C.DocumentAdder();
JSONContent.name = "sth";
JSONContent._filename = filebytes;
HttpContent _JSONContent = new StringContent("uploadManifest={\"input\": " + JsonConvert.SerializeObject(JSONContent).ToString() + "}", Encoding.UTF8, "application/json");
content.Add(_JSONContent);
// File Content in bytes
var fileContent = new ByteArrayContent(filebytes);
fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("_filename") { FileName = filepath };
//fileContent.ReadAsByteArrayAsync();
content.Add(fileContent);
// Request
HttpResponseMessage reponse;
var _Method = new HttpMethod("POST");
reponse = client.PostAsync(_Uri, content).Result;
// Request response
rcontent = reponse.Content.ReadAsStringAsync().Result;
textBox2.Text = reponse.ToString() + Environment.NewLine + rcontent.ToString();
}
}
}
But this is what I got in response:
StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: close
Cache-Control: no-store, must-revalidate, no-cache
Date: Mon, 26 Nov 2018 12:50:09 GMT
Server: Apache/2.4.29
Server: (Ubuntu)
Content-Length: 61
Content-Type: application/json; charset=UTF-8
Expires: Mon, 26 Jul 1997 05:00:00 GM
}
With:
["ERROR_UPLOAD_FILE_TOO_BIG_POST_MAX_SIZE","The file seems too big"]
The file I'm trying to upload is 592bytes! Max overall limit in one request is 2Mo. And post_max_size in php.ini is "8M", the same result after I changed it to "0" (for no limit at all). And then set it to 20M to match upload_max_filesize (/etc/php/7.2/apache2/php.ini).
upload_max_filesize_.. is also "20M"
If anyone finding this post and needs help, here is how i managed to succeed :
After separatly creating a "Session-Token", and using "RestSharp".
// Upload
var RSClient = new RestClient(Properties.Settings.Default.GLPI_URL);
var request = new RestRequest("Document", Method.POST);
request.AddHeader("Session-Token", Properties.Settings.Default.GLPI_SESSION_TOKEN);
request.AddHeader("App-Token", Properties.Settings.Default.GLPI_APP_TOKEN);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "multipart/form-data");
request.AddQueryParameter("uploadManifest", "{\"input\": {\"name\": \"UploadFileTest\", \"_filename\": \"GiletsJaunes.jpg\"}}");
request.AddFile("test", #"C:\path\to\File.jpg");
IRestResponse response = RSClient.Execute(request);
var content = response.Content;
textBox2.Text = textBox2.Text + Environment.NewLine + content;
Details :
I couldn't use RestSharp.Authenticator = new SimpleAuthenticator for some reasons, so i added these Auth params with AddHeader.
I couldn't use a Serialised Json string in a new StringContent, because of AddQueryParameter, so i wrote it manually.
Alleluyah.
Lately i have been trying to post a file from .net controller through .net controller using HttpClient,
to a Java Rest Service and failed many times as i was unable to send the file in the correct format.
below ajax call in the working jQuery way to invoke the service and pass the file with meta data key.
function Upload() {
var data = new FormData();
data.append('file', document.getElementById("file").files[0])
var metaData = [{"symbolicName": "DocumentTitle","dataType": "string","value": "Test CSEPF Document"}]
data.append('metaData', JSON.stringify(metaData));
$.ajax({
url: 'http://xxx:xx/FileNetCoreRestServices/AddDocument',
type: "POST",
dataType: 'json',
data: data,
contentType: false,
processData: false,
success: function (data) {
debugger
}
});
}
Request payload when posted with ajax
------WebKitFormBoundaryaCyKxSim0zzwhHK7
Content-Disposition: form-data; name="file"; filename="4.pdf"
Content-Type: application/pdf
------WebKitFormBoundaryaCyKxSim0zzwhHK7
Content-Disposition: form-data; name="metaData"
[{"symbolicName":"DocumentTitle","dataType":"string","value":"Test CSEPF Document"}]
------WebKitFormBoundaryaCyKxSim0zzwhHK7--
Now i want the same thing to be replicated in the .net controller side,
As per my research i know that i need to use MultipartFormDataContent and HttpClient in order to get this working.
below is the .net code i have written in order to upload the file to the service.
List<string> lstFilesToBeUploaded = null;
try
{
string FileTransferApiUrl = "http://xxx:xx/FileNetCoreRestServices/AddDocument";
lstFilesToBeUploaded = new List<string>();
string OperatorSourceRootFolderPath = Server.MapPath(System.Configuration.ConfigurationSettings.AppSettings["UploadLocation"]);
if (Directory.Exists(OperatorSourceRootFolderPath))
{
lstFilesToBeUploaded = Directory.GetFiles(OperatorSourceRootFolderPath).ToList();
}
foreach (string filePart in lstFilesToBeUploaded) // improvement - this is sequential, can be made threaded
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("metaData", "[{'symbolicName':'DocumentTitle','dataType':'string','value':'Test CSEPF Document'}]");
using (var content = new MultipartFormDataContent())
{
byte[] Bytes = System.IO.File.ReadAllBytes(filePart);
var fileContent = new StreamContent(new MemoryStream(Bytes));
fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName = Path.GetFileName(filePart) };
content.Add(fileContent);
var response = client.PostAsync(FileTransferApiUrl, fileContent).Result;
if (response.IsSuccessStatusCode)
{
var responseData = response.Content.ReadAsStringAsync().Result;
}
}
}
}
}
catch (Exception ex)
{
throw ex;
}
When i run this code i get the response as
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: { Access-Control-Allow-Origin: * $WSEP: Transfer-Encoding: chunked Connection: Close Date: Wed, 27 Jun 2018 11:41:39 GMT Server: WebSphere Server: Application Server: Server/7.0 Content-Language: en-US Content-Type: text/html; charset=ISO-8859-1 }}
I am not entirely sure how the MultipartFormDataContent works, it would be helpful if any one points out where i am going wrong.
After so many failed attempts and research i was able to call the service with the file, and below is the working code which i used in order to achieve this.
#region FILENET Upload
string FileLocation = Server.MapPath(System.Configuration.ConfigurationSettings.AppSettings["FileLocation"]);
var FileNetRestServiceURL = "http://xxx:xx/FileNetCoreRestServices/AddDocument";
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
//Working - Default Values
//var values = new[]
//{new KeyValuePair<string, string>("metaData", "[{'symbolicName':'DocumentTitle','dataType':'string','value':'Test CSEPF Document'}]")};
//Real Values
var values = new[]
{new KeyValuePair<string, string>("metaData", "[{'symbolicName':'"+Path.GetFileNameWithoutExtension(FileLocation).ToString()+"','dataType':'string','value':'"+Path.GetFileNameWithoutExtension(FileLocation).ToString()+"'}]")};
//Convert the file into ByteArrayContent as the service is expecting a file object
content.Add(new ByteArrayContent(System.IO.File.ReadAllBytes(FileLocation)), "file", Path.GetFileName(FileLocation).ToString());
foreach (var keyValuePair in values)
{
content.Add(new StringContent(keyValuePair.Value), keyValuePair.Key);
}
var response = client.PostAsync(FileNetRestServiceURL, content).Result;
if (response.IsSuccessStatusCode)
{
var responseData = response.Content.ReadAsStringAsync().Result;
}
}
}
#endregion
try something like:
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
var values = new[]
{
new KeyValuePair<string, string>("metaData", JsonConvert.SerializeObject("[{'symbolicName':'DocumentTitle','dataType':'string','value':'Test CSEPF Document'}]"))
};
foreach (var keyValuePair in values)
{
content.Add(new StringContent(keyValuePair.Value), keyValuePair.Key);
}
var fileContent = new ByteArrayContent(System.IO.File.ReadAllBytes(filePart));
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Foo.txt"
};
content.Add(fileContent);
var requestUri = "http://xxxx:xxxx/CSEPFCoreRestServices/addDocument";
var result = client.PostAsync(requestUri, content).Result;
}
}
I'm trying to post a multipart/form-data Using HttpClient
the form requires a certain number of images.
Code :
var client = new System.Net.Http.HttpClient();
var content = new MultipartFormDataContent();
var postData = new List<KeyValuePair(string,string)> ();
postData.Add(new KeyValuePair < string, string > ("function", "picture2"));
postData.Add(new KeyValuePair < string, string > ("username ", UserID));
postData.Add(new KeyValuePair < string, string > ("password ", Password));
foreach(var keyValuePair in postData) {
content.Add(new StringContent(keyValuePair.Value),
String.Format("\"{0}\"", keyValuePair.Key));
}
int x = 1;
foreach(Bitmap item in newpics) {
using(MemoryStream ms = new MemoryStream()) {
item.Save(ms, ImageFormat.Bmp);
byte[] bits = ms.ToArray();
content.Add(new ByteArrayContent(bits), '"' + "pict" + x + '"');
x += 1;
}
}
The problem is that only the last image is delivered !!
why does this happen?? what did i miss? and how to fix this problem?
Thanks in advance..
This is an example of how to post string and file stream with HTTPClient using MultipartFormDataContent. The Content-Disposition and Content-Type need to be specified for each HTTPContent:
Here's my example. Hope it helps:
var path = #"C:\B2BAssetRoot\files\596086\596086.1.mp4";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "CBS Brightcove API Service");
using (var content = new MultipartFormDataContent())
{
string assetName = Path.GetFileName(path);
var request = new HTTPBrightCoveRequest()
{
Method = "create_video",
Parameters = new Params()
{
CreateMultipleRenditions = "true",
EncodeTo = EncodeTo.Mp4.ToString().ToUpper(),
Token = "x8sLalfXacgn-4CzhTBm7uaCxVAPjvKqTf1oXpwLVYYoCkejZUsYtg..",
Video = new Video()
{
Name = assetName,
ReferenceId = Guid.NewGuid().ToString(),
ShortDescription = assetName
}
}
};
//Content-Disposition: form-data; name="json"
var stringContent = new StringContent(JsonConvert.SerializeObject(request));
stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\"");
content.Add(stringContent, "json");
FileStream fs = File.OpenRead(path);
var streamContent = new StreamContent(fs);
streamContent.Headers.Add("Content-Type", "application/octet-stream");
//Content-Disposition: form-data; name="file"; filename="C:\B2BAssetRoot\files\596090\596090.1.mp4";
streamContent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + Path.GetFileName(path) + "\"");
content.Add(streamContent, "file", Path.GetFileName(path));
//content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
Task<HttpResponseMessage> message = client.PostAsync("http://api.brightcove.com/services/post", content);
var input = message.Result.Content.ReadAsStringAsync();
Console.WriteLine(input.Result);
Console.Read();