I am trying to look at the fields of a Multipart upload and based on the contents of the string fields decide if I want to extract the attached files or not.
So it seemed reasonable to create a Custom MultiPartFormDataStreamprovider and check in there but when I try to copy the data via CopyToAsync pointing at a filestream I get no data. It seems the stream has already been read. What am I doing wrong?
internal class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
isValidFields _validateFields;
internal CustomMultipartFormDataStreamProvider(string path, isValidFields validateFields) : base(path) { _validateFields = validateFields; }
private readonly Collection _isFormData = new Collection();
// private readonly NameValueCollection _formData = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
//public NameValueCollection FormData
//{
//get { return _formData; }
//}
public override async Task ExecutePostProcessingAsync()
{
for (var index = 0; index < Contents.Count; index++)
{
if (IsStream(index))
continue;
var formContent = Contents[index];
var contentDisposition = formContent.Headers.ContentDisposition;
var formFieldName = UnquoteToken(contentDisposition.Name) ?? string.Empty;
var formFieldValue = await formContent.ReadAsStringAsync();
FormData.Add(formFieldName, formFieldValue);
}
if (_validateFields != null)
{
if (_validateFields(FormData) == false)
{
throw new Exception("Invalid data");
}
}
for (var index = 0; index < Contents.Count; index++)
{
if (!IsStream(index))
continue;
var formContent = Contents[index];
var contentDisposition = formContent.Headers.ContentDisposition;
var formFieldName = UnquoteToken(contentDisposition.Name) ?? string.Empty;
FileStream fileStream = null;
string pathname = Path.Combine(base.RootPath, Path.GetFileName(GetLocalFileName(formContent.Headers)));
using (fileStream = new FileStream(pathname, FileMode.Create, FileAccess.Write, FileShare.None))
{
try
{
await formContent.CopyToAsync(fileStream).ContinueWith((copyTask) =>
{
fileStream.Close();
});
}
catch (Exception ex)
{
throw;
}
}
}
}
private static string UnquoteToken(string token)
{
if (string.IsNullOrWhiteSpace(token))
return token;
if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1)
return token.Substring(1, token.Length - 2);
return token;
}
public bool IsStream(int idx)
{
return !_isFormData[idx];
}
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
if (parent == null) throw new ArgumentNullException("parent");
if (headers == null) throw new ArgumentNullException("headers");
var contentDisposition = headers.ContentDisposition;
if (contentDisposition != null)
{
_isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName));
Stream result = base.GetStream(parent, headers);
return result;
}
throw new InvalidOperationException("Did not find required 'Content-Disposition' header field in MIME multipart body part.");
}
public override string GetLocalFileName(HttpContentHeaders headers)
{
return headers.ContentDisposition.FileName.Replace("\"", string.Empty);
}
}
this is called by
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string guid = Guid.NewGuid().ToString();
string fileSaveLocation = HttpContext.Current.Server.MapPath("~/App_Data");
if (subDir.Length > 0)
Path.Combine(fileSaveLocation, subDir);
fileSaveLocation = Path.Combine(fileSaveLocation, guid);
Directory.CreateDirectory(fileSaveLocation);
CustomMultipartFormDataStreamProvider provider = new CustomMultipartFormDataStreamProvider(fileSaveLocation, validateFields);
try
{
// Read all contents of multipart message into CustomMultipartFormDataStreamProvider and allow validation of fields
await Request.Content.ReadAsMultipartAsync(provider);
okay by using the MultipartMemoryStreamProvider the xtraction to file can be held off and the functionality of MultiPartFormDataStreamprovider replicated, but with more control.
Related
I am uploading a file and wish to name it with one of the input fields which I wille be typing in my view. For ex: I type "Test" in my "Designation Commerciale" field. However, it gives me the NullReferenceException as it does not find any. Would appreciate your help. Thanks.
Controller:
public async Task<string> UploadFile(IFormFile file)
{
//bool iscopied;
string resp = String.Empty;
try
{
if (file.Length > 0)
{
var model = new IdentificationProduitViewModel();
string x = model.DesignationCommerciale;
string filename = x + Path.GetExtension(file.FileName);
string path = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "UploadFds"));
using (var filestream = new FileStream(Path.Combine(path, filename), FileMode.Create))
{
await file.CopyToAsync(filestream);
}
//iscopied = true;
resp = filename;
}
else
{
// iscopied = false;
resp = String.Empty;
}
}
catch(Exception)
{
throw;
}
return resp;
}
HTTPPOST:
string tryupload = await UploadFile(file_fds);
if (!String.IsNullOrEmpty(tryupload))
{
TempData["uploadok"] = "Fichier chargé avec success !";
model.Fds_Filepath = tryupload;
}
Net core application. I have one rest API which will send files to another API.
Below is the logic inside first API to send files to second API.
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await _tokenService.GetToken());
MultipartFormDataContent multipartFormData = new MultipartFormDataContent();
string contentJson = JsonConvert.SerializeObject(request);
HttpContent data = new StringContent(contentJson, Encoding.UTF8, "application/json");
multipartFormData.Add(data, "data");
foreach (var file in fileList)
{
if (file.Length <= 0)
continue;
var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
multipartFormData.Add(new StreamContent(file.OpenReadStream())
{
Headers =
{
ContentLength = file.Length,
ContentType = new MediaTypeHeaderValue(file.ContentType)
}
}, "File", fileName);
}
try
{
var response = await client.PostAsync("https://localhost:44370/apisendfile", multipartFormData);
}
catch (Exception ex)
{
}
}
I have second API as below
public async Task<ActionResult> SendMail([FromBody] MultipartFormDataContent formDataContent)
{
}
When I debug in my first API I receive error
Unsupported Media Type
I am trying all the way to figure it out but could not succeed. Can someone help me to identify this issue. Any help would be appreciated. Thanks
Well, you could try following way,
Web API Controller:
[HttpPost]
public string UploadMultipartFile()
{
var file = HttpContext.Current.Request.Files.Count > 0 ?
HttpContext.Current.Request.Files[0] : null;
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(
HttpContext.Current.Server.MapPath("~/MrPerfectMaltipartFolder"),
fileName
);
file.SaveAs(path);
}
return file != null ? "/MrPerfectMaltipartFolder/" + file.FileName : null;
}
Folder Location:
Tested on Post Man:
Open Folder Location:
File Uploaded:
For N Type of Multipart Data Upload:
[HttpPost]
public object UploadMultipartFileList()
{
var uploadedFilesName = new List<string>();
if (HttpContext.Current.Request.Files.Count > 0)
{
int count = 0;
foreach (var item in HttpContext.Current.Request.Files)
{
var getFile = HttpContext.Current.Request.Files[count];
if (getFile != null)
{
var fileName = Path.GetFileName(getFile.FileName);
var path = Path.Combine(
HttpContext.Current.Server.MapPath("~/MrPerfectMaltipartFolder"),
fileName
);
getFile.SaveAs(path);
}
count++;
string file = "/MrPerfectMaltipartFolder/" + getFile.FileName;
uploadedFilesName.Add(file);
}
}
return uploadedFilesName;
}
Output:
Example Both Data and File:
[HttpPost]
public object UploadMultipartFileList()
{
HttpRequest multipartRquest = HttpContext.Current.Request;
//General Data Part
string engineerName = multipartRquest.Form["EngineerName"];
string engineerEmail = multipartRquest.Form["EngineerEmail"];
//File Upload Part
var FilesName = new List<string>();
if (HttpContext.Current.Request.Files.Count > 0)
{
int count = 0;
foreach (var item in HttpContext.Current.Request.Files)
{
var getFile = HttpContext.Current.Request.Files[count];
if (getFile != null)
{
var fileName = Path.GetFileName(getFile.FileName);
var path = Path.Combine(
HttpContext.Current.Server.MapPath("~/MrPerfectMaltipartFolder"),
fileName
);
getFile.SaveAs(path);
}
count++;
string file = "/MrPerfectMaltipartFolder/" + getFile.FileName;
FilesName.Add(file);
}
}
return FilesName;
}
Request Format:
Output:
Hope it would resolve your problem. Feel free to share if you still encounter any issues.
Correct API style :
[HttpPost("logfiles")] // etc
public async Task<IActionResult> AddLogFile(IFormFile reportFile)
{
//UploadFile with any options with correct property Name = "ReportFile"
// all assigment will do framework itself in default impl
if (reportFile == null || reportFile.Length <= 0)
{
Submit client side style :
using (MultipartFormDataContent httpContent = new MultipartFormDataContent())
{
// read bytes from memstream etc
//...
httpContent.Add((HttpContent) bytes, "ReportFile", "test.log");
I need to upload files from client machine to a remote server and for that i have created a windows application which will work as client. It will select the required file and call the Web API.
Please find my client code as below :
//...other code removed for brevity
private void UploadFile(string filename)
{
Stream ms = new MemoryStream();
using (FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
var fileInfo = new FileInfo(filename);
byte[] bytes = new byte[file.Length];
file.Read(bytes, 0, (int)file.Length);
ms.Write(bytes, 0, (int)file.Length);
MultiPartFormUpload multiPartFormUpload = new MultiPartFormUpload();
List<FileInfo> files = new List<FileInfo>() { fileInfo };
try
{
MultiPartFormUpload.UploadResponse response = multiPartFormUpload.Upload("http://localhost:10458/api/Upload", files);
}
catch (Exception ex)
{
throw ex;
}
}
}
public class MultiPartFormUpload
{
public class MimePart
{
NameValueCollection _headers = new NameValueCollection();
byte[] _header;
public NameValueCollection Headers
{
get { return _headers; }
}
public byte[] Header
{
get { return _header; }
}
public long GenerateHeaderFooterData(string boundary)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("--");
stringBuilder.Append(boundary);
stringBuilder.AppendLine();
foreach (string key in _headers.AllKeys)
{
stringBuilder.Append(key);
stringBuilder.Append(": ");
stringBuilder.AppendLine(_headers[key]);
}
stringBuilder.AppendLine();
_header = Encoding.UTF8.GetBytes(stringBuilder.ToString());
return _header.Length + Data.Length + 2;
}
public Stream Data { get; set; }
}
public class UploadResponse
{
public UploadResponse(HttpStatusCode httpStatusCode, string responseBody)
{
HttpStatusCode = httpStatusCode;
ResponseBody = responseBody;
}
public HttpStatusCode HttpStatusCode { get; set; }
public string ResponseBody { get; set; }
}
public UploadResponse Upload(string url, List<FileInfo> files)
{
using (WebClient client = new WebClient())
{
List<MimePart> mimeParts = new List<MimePart>();
try
{
foreach (FileInfo file in files)
{
MimePart part = new MimePart();
string name = file.Extension.Substring(1);
string fileName = file.Name;
part.Headers["Content-Disposition"] = "form-data; name=\"" + name + "\"; filename=\"" + fileName + "\"";
part.Headers["Content-Type"] = "application/octet-stream";
part.Data = new MemoryStream(File.ReadAllBytes(file.FullName));
mimeParts.Add(part);
}
string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
client.Headers.Add(HttpRequestHeader.ContentType, "multipart/form-data; boundary=" + boundary);
long contentLength = 0;
byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");
foreach (MimePart mimePart in mimeParts)
{
contentLength += mimePart.GenerateHeaderFooterData(boundary);
}
byte[] buffer = new byte[8192];
byte[] afterFile = Encoding.UTF8.GetBytes("\r\n");
int read;
using (MemoryStream memoryStream = new MemoryStream())
{
foreach (MimePart mimePart in mimeParts)
{
memoryStream.Write(mimePart.Header, 0, mimePart.Header.Length);
while ((read = mimePart.Data.Read(buffer, 0, buffer.Length)) > 0)
memoryStream.Write(buffer, 0, read);
mimePart.Data.Dispose();
memoryStream.Write(afterFile, 0, afterFile.Length);
}
memoryStream.Write(_footer, 0, _footer.Length);
byte[] responseBytes = client.UploadData(url, memoryStream.ToArray());
string responseString = Encoding.UTF8.GetString(responseBytes);
return new UploadResponse(HttpStatusCode.OK, responseString);
}
}
catch (Exception ex)
{
foreach (MimePart part in mimeParts)
if (part.Data != null)
part.Data.Dispose();
if (ex.GetType().Name == "WebException")
{
WebException webException = (WebException)ex;
HttpWebResponse response = (HttpWebResponse)webException.Response;
string responseString;
using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
responseString = reader.ReadToEnd();
}
return new UploadResponse(response.StatusCode, responseString);
}
else
{
throw;
}
}
}
}
}
Please find Web API code as below :
public async Task<HttpResponseMessage> Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string fileSaveLocation = HttpContext.Current.Server.MapPath("~/upload/files");
CustomMultipartFormDataStreamProvider provider = new CustomMultipartFormDataStreamProvider(fileSaveLocation);
List<string> files = new List<string>();
try
{
await Request.Content.ReadAsMultipartAsync(provider);
foreach (MultipartFileData file in provider.FileData)
{
files.Add(Path.GetFileName(file.LocalFileName));
}
// Send OK Response along with saved file names to the client.
return Request.CreateResponse(HttpStatusCode.OK, files);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public CustomMultipartFormDataStreamProvider(string path) : base(path) { }
public override string GetLocalFileName(HttpContentHeaders headers)
{
return headers.ContentDisposition.FileName.Replace("\"", string.Empty);
}
}
I am able to hit the Web API with the client code but fileInfo.Length is coming 0.
Please let me know what i am missing in Client or Web API code. Thanks !
I'm using this code to upload multiple files and it working very well. It uses modernhttpclient library.
public async Task<string> PostImages (int platform, string url, List<byte []> imageList)
{
try {
int count = 1;
var requestContent = new MultipartFormDataContent ();
foreach (var image in imageList) {
var imageContent = new ByteArrayContent (image);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse ("image/jpeg");
requestContent.Add (imageContent, "image" + count, "image.jpg");
count++;
}
var cookieHandler = new NativeCookieHandler ();
var messageHandler = new NativeMessageHandler (false, false, cookieHandler);
cookieHandler.SetCookies (cookies);
using (var client = new HttpClient (messageHandler)) {
client.DefaultRequestHeaders.TryAddWithoutValidation ("User-Agent", GetUserAgent (platform));
using (var r = await client.PostAsync (url, requestContent)) {
string result = await r.Content.ReadAsStringAsync ();
System.Diagnostics.Debug.WriteLine ("PostAsync: " + result);
return result;
}
}
} catch (Exception e) {
System.Diagnostics.Debug.WriteLine (e.Message);
return null;
}
}
Now I need the progress when uploading the files. I searched in google and found I need to use ProgressStreamContent
https://github.com/paulcbetts/ModernHttpClient/issues/80
Since ProgressStreamContent contains a constructor that takes a stream, I converted the MultipartFormDataContent to stream and used it in its constructor. But, its not working. Upload fails. I think its because it is a stream of all the files together which is not what my back end is expecting.
public async Task<string> PostImages (int platform, string url, List<byte []> imageList)
{
try {
int count = 1;
var requestContent = new MultipartFormDataContent ();
// here you can specify boundary if you need---^
foreach (var image in imageList) {
var imageContent = new ByteArrayContent (image);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse ("image/jpeg");
requestContent.Add (imageContent, "image" + count, "image.jpg");
count++;
}
var cookieHandler = new NativeCookieHandler ();
var messageHandler = new NativeMessageHandler (false, false, cookieHandler);
cookieHandler.SetCookies (RestApiPaths.cookies);
var stream = await requestContent.ReadAsStreamAsync ();
var client = new HttpClient (messageHandler);
client.DefaultRequestHeaders.TryAddWithoutValidation ("User-Agent", RestApiPaths.GetUserAgent (platform));
var request = new HttpRequestMessage (HttpMethod.Post, url);
var progressContent = new ProgressStreamContent (stream, 4096);
progressContent.Progress = (bytes, totalBytes, totalBytesExpected) => {
Console.WriteLine ("Uploading {0}/{1}", totalBytes, totalBytesExpected);
};
request.Content = progressContent;
var response = await client.SendAsync (request);
string result = await response.Content.ReadAsStringAsync ();
System.Diagnostics.Debug.WriteLine ("PostAsync: " + result);
return result;
} catch (Exception e) {
System.Diagnostics.Debug.WriteLine (e.Message);
return null;
}
}
What should I do here to get this working? Any help is appreciated
I have a working version of ProgressableStreamContent. Please note, I am adding headers in the constructor, this is a bug in original ProgressStreamContent that it does not add headers !!
internal class ProgressableStreamContent : HttpContent
{
/// <summary>
/// Lets keep buffer of 20kb
/// </summary>
private const int defaultBufferSize = 5*4096;
private HttpContent content;
private int bufferSize;
//private bool contentConsumed;
private Action<long,long> progress;
public ProgressableStreamContent(HttpContent content, Action<long,long> progress) : this(content, defaultBufferSize, progress) { }
public ProgressableStreamContent(HttpContent content, int bufferSize, Action<long,long> progress)
{
if (content == null)
{
throw new ArgumentNullException("content");
}
if (bufferSize <= 0)
{
throw new ArgumentOutOfRangeException("bufferSize");
}
this.content = content;
this.bufferSize = bufferSize;
this.progress = progress;
foreach (var h in content.Headers) {
this.Headers.Add(h.Key,h.Value);
}
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
return Task.Run(async () =>
{
var buffer = new Byte[this.bufferSize];
long size;
TryComputeLength(out size);
var uploaded = 0;
using (var sinput = await content.ReadAsStreamAsync())
{
while (true)
{
var length = sinput.Read(buffer, 0, buffer.Length);
if (length <= 0) break;
//downloader.Uploaded = uploaded += length;
uploaded += length;
progress?.Invoke(uploaded, size);
//System.Diagnostics.Debug.WriteLine($"Bytes sent {uploaded} of {size}");
stream.Write(buffer, 0, length);
stream.Flush();
}
}
stream.Flush();
});
}
protected override bool TryComputeLength(out long length)
{
length = content.Headers.ContentLength.GetValueOrDefault();
return true;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
content.Dispose();
}
base.Dispose(disposing);
}
}
Also note, it expects HttpContent, not stream.
This is how you can use it.
var progressContent = new ProgressableStreamContent (
requestContent,
4096,
(sent,total) => {
Console.WriteLine ("Uploading {0}/{1}", sent, total);
});
I uploaded the image to Amazon S3 Successfully but when I view the image it shows nothing.
My View :
public JsonResult Upload(SocialJobMediaModel socialJobMediaModel)
{
var imagePathErrorMessage = new ImagePathErrorMessage();
if (Request.Files.Count > 0)
{
_log = GetProcessorLogger("CompanyXcodes", "CompanyXcodes", 1);
StageMediaFileProcessor stageMediaFileProcessor = new StageMediaFileProcessor(_log);
imagePathErrorMessage = stageMediaFileProcessor.UploadMediaFilesToS3(Request.Files[0].InputStream, Request.Files[0].FileName,"1234");
}
}
public void BulkUpload(Stream stream, string fileName, NameValueCollection metadata, ILogger log = null)
{
// Prepare request
if (string.IsNullOrEmpty(AwsRegion))
{
Client = new AmazonS3Client(_awsAccessKey, _awsSecretKey);
}
else
{
var regionEndPoint = RegionEndpoint.GetBySystemName(AwsRegion);
Client = new AmazonS3Client(regionEndPoint);
}
var tu = new TransferUtility(Client);
var request = new TransferUtilityUploadRequest
{
InputStream = stream,
Key = KeyPath + fileName,
BucketName = BucketName,
CannedACL = S3CannedACL.PublicRead,
};
if (metadata != null)
{
foreach (var metaDataKey in metadata.AllKeys)
{
request.Metadata.Add(metaDataKey, metadata[metaDataKey]);
}
}
// Set up progress logging (if logger provided)
if (log != null)
request.UploadProgressEvent += (sender, e) =>
{
if (e.PercentDone % 10 == 0)
log.LogInfo("Uploaded {0} of {1} bytes ({2} %)", e.TransferredBytes, e.TotalBytes, e.PercentDone);
};
// Upload (synchronous)
tu.Upload(request);
}
I am able to upload the image Successfully but when I view the image it shows nothing.can any one help me on this
I tried to upload filepath instead of inputstream but it throws the error