Trying to get file response from ASP.NET Web API - c#

I have migrated some methods from a MVC controller to a Web API controller and now I have this method:
[HttpPost]
[Route("api/Request/UploadImage")]
public IHttpActionResult UploadImage()
{
try
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var httpRequest = System.Web.HttpContext.Current.Request;
if (_inMemoryStore == null)
{
_inMemoryStore = new List<FileLocalStore>();
}
if (httpRequest.Files.Count > 0)
{
var postedFile = httpRequest.Files[0];
var uniqueFileName = Guid.NewGuid().ToString();
var fileStream = new MemoryStream();
postedFile.InputStream.CopyTo(fileStream);
_inMemoryStore.Add(new FileLocalStore() { Id = uniqueFileName, File = fileStream });
var fileStore = new ServiceRequestAttachmentViewModel
{
FileName = httpRequest.Form["FileName"].ToString(),
FileMIME = httpRequest.Form["FileMIME"].ToString(),
UniqueFileName = uniqueFileName,
Thumbnail = fileStream.GetBuffer().GetThumbnailImage(80, 80),
IsPrivate = Convert.ToBoolean(httpRequest.Form["IsPrivate"].ToString()),
IsAdded = true,
IsDeleted = false
};
var content = JsonConvert.SerializeObject(fileStore);
// return Ok(fileStore);
return Content(HttpStatusCode.OK,fileStore);
}
else
{
return Ok(new { Data = "" });
//return Request.CreateResponse(HttpStatusCode.Created, new
//{ Data = "" });
}
}
catch (Exception ex)
{
Log.Error($"Error uploading image {ex.Message} {ex.InnerException} {ex.StackTrace}");
return BadRequest(ex.Message);
//var response2 = Request.CreateResponse(HttpStatusCode.BadRequest, ex.Message);
//return response2;
}
}
In the original MVC controller, it was easy to return the ContentResult after fileStore was serialized. I want to do the same thing here but I'm having issues. It keeps saying it exceeds the maximum bytes but the file was only 10k big.
Is there something else I need to set? the media type received is a multipart/form-data type. The thumbnail property is the issue as it has bytes of data.
This is being called using fileupload() method of jQuery.

You probably net to update the maxRequestLength & maxAllowedContentLength in web.config
From MSDN, the maximum default size is 4MB
Here's the setting for 1GB
<system.web>
<httpRuntime maxRequestLength="2097152" requestLengthDiskThreshold="2097152" executionTimeout="240"/>
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="2147483648" />
</requestFiltering>
</security>
</system.webServer>

Related

uploading and accessing images into asp .net api folder using angular

I have angular project as my client side and .Net 6 web api project as my backend. I am still new to both technologies. I am creating a website and there is a functionality that I am trying to add and haven't been successful so far. I want to upload images into a .Net web api project images folder using angular. I also want to later access those images from angular project. I want to store the path of the image files in the database. I have tried to check for the code on the internet without success. Your assistance will be appreciated.
First, submit your's files from frontend with FormData
postWithFile(url: string, obj: any, files: File[]) {
let cloneHeader: any = {};
let options: any = {
headers: new HttpHeaders(cloneHeader),
observe: 'response',
responseType: 'json'
};
let formData: FormData = new FormData();
if (typeof obj == 'object') { // obj is external submit data
formData.append('data', JSON.stringify(obj));
} else {
formData.append('data', obj);
}
if (files && files.length > 0) {
files.forEach((ds, index) => {
formData.append('file_' + index, ds, ds.name);
});
}
return this._http
.post(this.host + url, formData, options)
.pipe(map((res: any) => {
return res.body;
}));
}
And backend handle request with HttpContext.Current.Request.Files, save images to server and store path of images in database
[HttpPost]
public ResponseMessage<bool?> UploadImages()
{
var response = new ResponseMessage<bool?>();
try
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
ExternalDataModel model = MessageConvert.DeserializeObject<ExternalDataModel>(HttpContext.Current.Request["data"]); // obj in frontend
//
List<string> listImages = new List<string>();
DateTime now = DateTime.Now;
string buildPath = $"{string.Format("{0:00}", now.Year)}\\{string.Format("{0:00}", now.Month)}\\{string.Format("{0:00}", now.Day)}"; // change by your's folder path
foreach (string file in HttpContext.Current.Request.Files)
{
var fileContent = HttpContext.Current.Request.Files[file];
int fileLength = fileContent.ContentLength;
if (fileContent != null && fileLength > 0)
{
var stream = fileContent.InputStream;
byte[] imgByteArray;
using (MemoryStream memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
imgByteArray = memoryStream.ToArray();
}
string fileName = $"format_file_name_if_need_{fileContent.FileName}";
string RelativeFolder = $"{buildPath}";
string AbsoluteFolder = Path.Combine("FOLDER_IN_SERVER_FULL_PATH", RelativeFolder);
if (!Directory.Exists(AbsoluteFolder))
{
Directory.CreateDirectory(AbsoluteFolder);
}
string pathSave = Path.Combine(RelativeFolder, fileName);
FileHelper.SaveFileFromBinaryArray(pathSave, imgByteArray);
listImages.Add(pathSave);
}
}
// model.listImages = listImages; // assign to model to save to DB
//
// var data = _bus.uploadImage(model);
// if (data)
// {
// response.Data = true;
// response.MessageCode = MessageCodes.UpdateSuccessfully;
// }
}
catch (Exception ex)
{
response.MessageCode = ex.Message;
}
return response;
}

List<IFormFile> Files always 0 Files count in AspNetCore 3.1 Web Api

I'm building a REST API application where I want to give an option to users they can upload single or multiple files, I checked with uploading files from Postman but in my controller, I always get zero files, Please help me to know what wrong I'm doing
Postman Image
Below is my code.
public async Task<IActionResult> UploadInvoice([FromForm(Name = "Files")] List <IFormFile> uploadFiles) {
try {
long size = uploadFiles.Sum(f => f.Length);
if (uploadFiles == null || uploadFiles.Count <= 0) {
Response.StatusCode = (int) HttpStatusCode.BadRequest;
return Content(JsonConvert.SerializeObject(new {
message = "No files found."
}), "application/json");
}
var filesToUpload = new List < FileUploadModel > ();
foreach(var file in uploadFiles) {
var fileGuid = Guid.NewGuid().ToString().Replace("-", "");
var path = Path.Combine(Directory.GetCurrentDirectory(), # "wwwroot\docs", $ "{fileGuid}{Path.GetExtension(file.FileName)}");
using(var fileStream = new FileStream(path, FileMode.Create)) {
await file.CopyToAsync(fileStream);
await fileStream.FlushAsync();
}
}
return Content(JsonConvert.SerializeObject(new {
message = ""
}), "application/json");
} catch (Exception ex) {
Response.StatusCode = (int) HttpStatusCode.InternalServerError;
return Content(JsonConvert.SerializeObject(new {
message = ex.Message
}), "application/json");
}
}

How to get Facebook profile Picture C# .Net

I need to get the user´s facebook profile picture and input it in a crop structure. I´m using Asp.NET MVC, jcrop and the facebook SDK. Untill now i can input files from my computer. I also have a function that access the facebook of the user and returns a session with the user Id, and a GetPhoto function that should return the profile picture. Can someone help me?
I use this code to input the files from the computer:
[ValidateAntiForgeryToken]
public ActionResult _Upload(IEnumerable<HttpPostedFileBase> files)
{
if (files == null || !files.Any()) return Json(new { success = false, errorMessage = "No file uploaded." });
var file = files.FirstOrDefault(); // get ONE only
if (file == null || !IsImage(file)) return Json(new { success = false, errorMessage = "File is of wrong format." });
if (file.ContentLength <= 0) return Json(new { success = false, errorMessage = "File cannot be zero length." });
var webPath = GetTempSavedFilePath(file);
//mistertommat - 18 Nov '15 - replacing '\' to '//' results in incorrect image url on firefox and IE,
// therefore replacing '\\' to '/' so that a proper web url is returned.
return Json(new { success = true, fileName = webPath.Replace("\\", "/") }); // success
}
i tried doing this but the GetPhoto() is returning a null element.
public ActionResult RetornoFb()
{
var _fb = new FacebookClient();
FacebookOAuthResult oauthResult;
if (!_fb.TryParseOAuthCallbackUrl(Request.Url, out oauthResult))
{
// Error
}
if (oauthResult.IsSuccess)
{
dynamic parameters = new ExpandoObject();
parameters.client_id = id;
parameters.redirect_uri = "http://localhost:4323/Avatar/RetornoFb/";
parameters.client_secret = secretkey;
parameters.code = oauthResult.Code;
dynamic result = _fb.Get("/oauth/access_token", parameters);
var accessToken = result.access_token;
Session.Add("FbUserToken", accessToken);
}
else
{
}
//return RedirectToAction("Upload");
HttpPostedFileBase objFile = (HttpPostedFileBase)new MemoryPostedFile(GetPhoto());
var webPath = GetTempSavedFilePath(objFile);
return Json(new { success = true, fileName = webPath.Replace("\\", "/") }); // success
}
public byte[] GetPhoto()
{
try
{
string url = "https://graph.facebook.com/" + GetProfileId() + "?fields=picture.width(480).height(480)";
WebClient webClient = new WebClient();
string response = webClient.DownloadString(url);
dynamic json = JObject.Parse(response);
string urlPicture = json.picture.data.url;
return webClient.DownloadData(urlPicture);
}
catch (Exception)
{
return null;
}
}
Resolved changing my GetPhoto Function. I was having permission issues.
private byte[] GetPhoto()
{
try
{
var _fb = new FacebookClient(Session["FbuserToken"].ToString());
dynamic resultMe = _fb.Get(GetProfileId()+"?fields=picture.width(480).height(480)");
WebClient webClient = new WebClient();
string urlPicture = resultMe.picture.data.url;
return webClient.DownloadData(urlPicture);
}
catch (Exception)
{
return null;
}
}

LargeJsonResult in MVC return many values

I have a controller which return many datas. then i got "Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property."
i have add my web.config using this. But the error is still occured.
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483645" recursionLimit="100">
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
then I add new class LargeJsonResult like on this website http://brianreiter.org/2011/01/03/custom-jsonresult-class-for-asp-net-mvc-to-avoid-maxjsonlength-exceeded-exception/
it said like this in controller
return new LargeJsonResult() { Data = output, MaxJsonLength = int.MaxValue };
but how can I use that with many return data? below is my controller
public ActionResult LoadInitData()
{
try
{
Database db = new Database("CON001");
_employee = Helper.Common.GetEmployeeData(db);
EmployeeDAC dacEmployee = new EmployeeDAC(db);
Employee dataEmployee = dacEmployee.GetDataByComputerLogin(GetUser());
if (_employee.SBU == "FB")
{
BrandBudgetDAC dacBrandBudget = new BrandBudgetDAC(db);
List<BrandBudget> dataBrandBudget = dacBrandBudget.GetDataBrandFB();
PostBudgetDAC dacPostBudget = new PostBudgetDAC(db);
List<PostBudget> dataPostBudget = dacPostBudget.GetDataPostFB();
AreaDAC dacArea = new AreaDAC(db);
List<Area> dataArea = dacArea.GetData();
return Json(new { Employee = dataEmployee, BrandBudget = dataBrandBudget, PostBudget = dataPostBudget, Area = dataArea }, JsonRequestBehavior.AllowGet);
}
else
{
BrandBudgetDAC dacBrandBudget = new BrandBudgetDAC(db);
List<BrandBudget> dataBrandBudget = dacBrandBudget.GetData(_employee.SBU);
PostBudgetDAC dacPostBudget = new PostBudgetDAC(db);
List<PostBudget> dataPostBudget = dacPostBudget.GetData(_employee.SBU);
AreaDAC dacArea = new AreaDAC(db);
List<Area> dataArea = dacArea.GetData();
return Json(new { Employee = dataEmployee, BrandBudget = dataBrandBudget, PostBudget = dataPostBudget, Area = dataArea }, JsonRequestBehavior.AllowGet);
}
}
catch (Exception ex)
{
return Json(new { Error = ex.Message }, JsonRequestBehavior.AllowGet);
}
}
To reduce the payload, consider making 4 separate ajax calls, each to a different method that returns one of the 4 properties you need.
public ActionResult LoadInitEmployeeData()
{
Employee dataEmployee = ....
....
return Json(dataEmployee, JsonRequestBehavior.AllowGet)
}
public ActionResult LoadBrandBudgetData()
{
List<BrandBudget> dataBrandBudget = ....
return Json(dataBrandBudget, JsonRequestBehavior.AllowGet)
}
etc.

How do I get a byte array from HttpInputStream for a docx file?

I am using the method from the first answer in this post: How to create byte array from HttpPostedFile but it doesn't work for .docx files for some reason.
//viewmodel.File is HttpPostedFileBase
byte[] fileData;
using (var binaryReader = new BinaryReader(viewModel.File.InputStream))
{
fileData = binaryReader.ReadBytes(viewModel.File.ContentLength);
}
On .docx files fileData shows as {byte[0]}, but it works with pdfs, excel files (xlsx), pre 2007 word files (doc), and images (i.e. the value is greater than zero). Saved to the database, the fileData is 0x.
How do I get a byte array from HttpInputStream for a docx file?
UPDATE
My web.config is configured with
<httpRuntime targetFramework="4.5" maxRequestLength="102400" />
This is working with xslx files bigger than 4MB but docx files less than 80KB are not.
UPDATE 2
I can get fileData to populate using the method explained here: http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.fileupload.postedfile.aspx
byte[] fileData = new byte[viewModel.File.ContentLength];
viewModel.File.InputStream.Read(fileData, 0, viewModel.File.ContentLength);
But if I saved that byte array to the database and try to write a file, it is severely corrupt. Saved to the database in this case it looks like 0x00000000000000000000000...
UPDATE 3
Here is the whole controller method, though I don't think seeing the whole thing is necessary:
[HttpPost]
public ActionResult SaveChangeFile(AttachmentFormViewModel viewModel)
{
if (viewModel.File == null)
return Json(new { success = false, message = "No file was found, please select a file and try again." }, "text/x-json",
JsonRequestBehavior.DenyGet);
try
{
//Validate that the right kind of File has been uploaded
OperationResponse response = _attachmentProcessor.ValidateAttachmentContentType(viewModel.File, (ChangeFileTypeEnum)viewModel.FileType);
if (!response.IsSuccess)
return Json(new { success = response.IsSuccess, message = response.Message }, "text/x-json", JsonRequestBehavior.DenyGet);
UpdateProjectFromCostCalculatorRequest projectValues = null;
Workbook workbook = null;
Document document = null;
if (_attachmentProcessor.IsWorkbook(viewModel.File))
workbook = new Workbook(viewModel.File.InputStream);
if (_attachmentProcessor.IsDocument(viewModel.File))
document = new Document(viewModel.File.InputStream);
var filename = Path.GetFileName(viewModel.File.FileName);
//if cost calc, validate that the values are correct and update related project
switch ((ChangeFileTypeEnum)viewModel.FileType)
{
case ChangeFileTypeEnum.CostCalculator:
response = _attachmentProcessor.ValidateCostCalculator(workbook, filename);
if (response.IsSuccess)
projectValues = _attachmentProcessor.GetDataFromCostCalculator(workbook);
break;
case ChangeFileTypeEnum.DataValidation:
response = _attachmentProcessor.ValidateDataValidation(workbook);
break;
case ChangeFileTypeEnum.WorkPaper:
response = _attachmentProcessor.ValidateWorkPaper(document);
break;
}
//return error message if any of the validations above failed
if (!response.IsSuccess)
return Json(new { success = response.IsSuccess, message = response.Message }, "text/x-json", JsonRequestBehavior.DenyGet);
//get the file from the stream and put into a byte[] for saving the database
byte[] fileData;
using (var binaryReader = new BinaryReader(viewModel.File.InputStream))
{
fileData = binaryReader.ReadBytes(viewModel.File.ContentLength);
}
var file = new ChangeFile
{
ChangeRequestID = viewModel.ChangeRequestId,
ChangeFileTypeID = viewModel.FileType,
File = fileData,
Filename = filename,
ContentType = viewModel.File.ContentType,
CreatedBy = User.UserNameWithoutDomain(),
UpdatedBy = User.UserNameWithoutDomain(),
CreatedDate = DateTime.Now,
UpdatedDate = DateTime.Now
};
_changeRequestService.SaveChangeFile(file);
var log = new ChangeFileImportLog { CreatedDate = DateTime.Now };
switch ((ChangeFileTypeEnum)viewModel.FileType)
{
case ChangeFileTypeEnum.CostCalculator:
var project = _changeRequestService.GetChangeProjectByPsrs(file.ChangeRequestID, projectValues.PsrsNumber);
if (project != null)
{
_attachmentProcessor.UpdateChangeProjectWithProjectValues(project, projectValues);
log.NumberOfErrors = 0;
log.NumberOfSegmentChanges = 0;
log.NumberOfWarnings = 0;
}
else
{
log.NumberOfWarnings = 1;
log.Warnings =
String.Format(
"There is no project on this Change Request with PSRS \"{0}\". If there was, the new cost would be updated with \"{1:C0}\"",
projectValues.PsrsNumber, projectValues.Cost);
}
break;
case ChangeFileTypeEnum.DataValidation:
log = _attachmentProcessor.CreateChangeSegmentsFromDataValidation(workbook, file.ChangeRequestID, file.ChangeFileID, User);
break;
case ChangeFileTypeEnum.WorkPaper:
log = _attachmentProcessor.UpdateChangeProjectsFromWorkPaper(document, file.ChangeRequestID, file.ChangeFileID,
User);
break;
}
log.CreatedBy = User.UserNameWithoutDomain();
log.CreatedDate = DateTime.Now;
log.UpdatedBy = User.UserNameWithoutDomain();
log.UpdatedDate = DateTime.Now;
_changeRequestService.SaveChangeFileImportLog(log, file.ChangeFileID);
_changeRequestService.Commit();
return Json(new { success = response.IsSuccess, message = response.Message }, "text/x-json", JsonRequestBehavior.DenyGet);
}
catch (Exception ex)
{
return Json(new { success = false, message = String.Format("A system error was encountered: {0}", ex) }, "text/x-json", JsonRequestBehavior.DenyGet);
}
}
Turns out that since I am using the stream already (see the controller method in the question), it is empty when I tried to save it.
I am not sure why I experienced this with docx and not xlsx since they both have their Streams consumed before the save. My guess is it has something to do with the differences in the Aspose.Cells and Aspose.Words implementations.
Regardless, however, I set the position on the stream back to 0, and it worked.
//viewmodel.File is HttpPostedFileBase
viewModel.File.InputStream.Position = 0; //<-----This fixed it!
byte[] fileData;
using (var binaryReader = new BinaryReader(viewModel.File.InputStream))
{
fileData = binaryReader.ReadBytes(viewModel.File.ContentLength);
}

Categories