In Asp.Net Core, how can I get the multipart/form-data from the body? - asp.net-core

In Asp.Net Core, it appears that they have done away with the Request.Content.ReadAsMultipartAsync functionality in favor of the IFormFile.
This makes uploading where you have an actual file a LOT easier, however, I have a use case where I need to upload a file to browser memory, process it, then send it as part of the multi-form data in the body. IFormFile cannot see this as there is no actual file to read. It only works if you have a filename property on the Content-Disposition and an actual file on the client to upload.
In my Asp.Net 4 app, I could read the mutlipart data in the body whether that was sent between boundaries or as an attached file.
How do I accomplish this in .Net Core?

What I figured out is that the multipart values are passed into the HttpRequest.Form as an array of key/value pairs. The "name" value on the body's multipart determines the name of the key.
I created a helper method that grabs both files and form values.
public static List<FileModel> GetFileModelsFromRequest(HttpRequest request)
{
var fileModels = new FileModels();
foreach (var formField in request.Form)
{
// Form data
var fileModelText = formField.Value;
... process and add to the FileModel list
}
if (request.Form.Files != null && request.Form.Files.Count > 0)
{
foreach (var file in request.Form.Files)
{
using (MemoryStream ms = new MemoryStream())
{
// File data
formFile.CopyTo(ms);
}
... process and add to the FileModel list
}
}
return fileModels;
}

I have done it this way. when I had to capture image from webcam and process (show that image in browser) it in browser memory and later on post that image using a form.
public IActionResult Index()
{
var files = HttpContext.Request.Form.Files;
if (files != null)
{
foreach (var file in files)
{
var fileName = file.Name;
}
}
return View();
}
I used a JS library Webcam.js to capture image from webcam and show that image on the same page. and once a user is satisfied with the image, s/he can upload the image to the server.
<!-- Configure settings and attach camera -->
<script language="JavaScript">
Webcam.set({
width: 320,
height: 240,
image_format: 'jpeg',
jpeg_quality: 90
});
Webcam.attach('#camera');
</script>
<!-- handle snapshot and displaying it locally -->
<script language="JavaScript">
function take_snapshot() {
// take snapshot and get image data
Webcam.snap(function (data_uri) {
// display results in page
document.getElementById('imageResults').innerHTML =
'<img src="' +
data_uri +
'"/>';
Webcam.upload(data_uri,
'/Default/Index',
function (code, text) {
console.log('Photo Captured');
});
});
}
</script>
<div class="panel panel-default">
<div class="panel-heading">Camera</div>
<div class="panel-body">
<div id="camera"></div>
<!-- A button for taking snaps -->
<form>
<input type="button" class="btn btn-success" value="Take Snapshot" onClick="take_snapshot()">
</form>
<div class="panel panel-default">
<div class="panel-heading">Captured Image</div>
<div class="panel-body">
<div id="imageResults">captured image will appear here...</div>
</div>
<br />
<br />
</div>
let me know if this is what you are looking for.

Related

Uploading asp.net core 3.1 Razor

I'm trying to upload files in ASP.NET core 3.1, but in my Post I'm not receiving the file.
That count = 0 is what I always get.
#cshtml
<input asp-for="app.FormFile" id="input-2" name="input2[]" type="file" class="file" multiple data-show-upload="true" data-show-caption="true">
#cshtml.cs
public async Task<IActionResult> OnPostAsync(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
var filePath = Path.GetTempFileName();
using (var stream = System.IO.File.Create(filePath))
{
await formFile.CopyToAsync(stream);
}
}
}
// Process uploaded files
// Don't rely on or trust the FileName property without validation.
return Ok(new { count = files.Count, size });
}
In ASP.NET core 2.0 this worked fine! What's wrong?
The param name is files, but you're explicitly setting the input name to input2[], so they don't match up. ASP.NET Core isn't going to try to interpret that you've uploaded some files so you probably want them to go on this param. If it can't find something to bind the data to (by name), it's just going to discard it and move on.
The name should files so it matches up to the param name: name="files".
even if I pass the name it still came with count = 0 <input asp-for="Laudos.FormFile" id="files" name="files" type="file" class="file" multiple data-show-upload="true" data-show-caption="true">
You can try to specify an encoding type (enctype) of multipart/form-data for your <form> tag, like below.
<form method="post" enctype="multipart/form-data">
<input asp-for="Laudos.FormFile" id="input-2" name="files" type="file" class="file" multiple data-show-upload="true" data-show-caption="true">
<input type="submit">
</form>
Test Result

Vuejs binding to img src only works on component rerender

I got a component that let's the user upload a profile picture with a preview before sending it off to cloudinary.
<template>
<div>
<div
class="image-input"
:style="{ 'background-image': `url(${person.personData.imagePreview})` } "
#click="chooseImage"
>
<span
v-if="!person.personData.imagePreview"
class="placeholder"
>
<i class="el-icon-plus avatar-uploader-icon"></i>
</span>
<input
type="file"
ref="fileInput"
#change="previewImage"
>
</div>
</div>
</template>
The methods to handle the preview:
chooseImage() {
this.$refs.fileInput.click()
},
previewImage(event) {
// Reference to the DOM input element
const input = event.target;
const files = input.files
console.log("File: ", input.files)
if (input.files && input.files[0]) {
this.person.personData.image = files[0];
const reader = new FileReader();
reader.onload = (e) => {
this.person.personData.imagePreview = e.target.result;
}
// Start the reader job - read file as a data url (base64 format)
reader.readAsDataURL(input.files[0]);
}
},
This works fine, except for when the user fetches a previous project from the DB. this.person.personData.imagePreview get's set to something like https://res.cloudinary.com/resumecloud/image/upload/.....id.jpg Then when the user wants to change his profile picture, he is able to select a new one from his local file system, and this.person.personData.imagePreview is read again as a data url with base64 format. But the preview doesn't work. Only when I change routes back and forth, the correct image selected by the user is displayed.
Like I said in the comment on my post. Turns out I'm an idiot. When displaying the preview, I used this.person.personData.imagePreview . When a user fetches a project from the DB, I just did this.person.personData = response.data. That works fine, apart from the fact that I had a different name for imagePreview on my backend. So I manually set it on the same load method when fetching from the DB like: this.person.personData.imagePreview = this.loadedPersonData.file. For some reason, that screwed with the reactivity of Vue.

Load partial view through controller on Search button click

I am working on an ASP.NET Core 2.1 MVC app using razor. I have searchQuery.cshtml and a (individually working perfectly) viewQuery.cshtml pages. In my searchQuery page, I let user enter queryId and on clicking "Search" button I want to run the action of ViewQuery that displays the results in viewQuery.cshtml and show the viewQuery below the search button area.
I am not good working with Ajax or so. On Search btn click, I call the viewQuery Get action thru ajax. In the button click, I pass the entered queryId of type int. But, when I load searchQuery page, it throws null exception for passing the queryId. I searched few hous, but didn't get any solution.
searchQuery.cshtml UPDATED
<div>
<div class="col-md-6">
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.QueryId)
</dt>
<dd>
<input asp-for="QueryId" class="form-control" />
</dd>
</dl>
</div>
<input type="submit" value="Show" />
<!-- CHANGE IN CALL -->
Search
</div>
<div class="modal fade" id="myModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
×
<h3 class="modal-title">Query Answer</h3>
</div>
<div class="modal-body" id="myModalBodyDiv">
</div>
<div class="modal-footer">
Ok
</div>
</div>
</div>
</div>
<script>
function ShowResult() {
// Retrieve queryId
var queryId = $("#QueryId").val();
// DisplayS PROPERLY
alert("Entered ID " + queryId);
// TRIED '/query/viewQuery' ALSO
$.ajax({
type: 'GET',
url: '../query/viewQuery',
data: { queryId: queryId },
success: function (response) {
alert(response); // **DISPLAYS [Object: object]**
$("#myModalBodyDiv").html(response);
$('#myModal').modal("show");
}, error: function (response) {
alert("Error: " + response);
}
});
}
</script>
My ViewQuery action in controller UPDATED
[Route("[controller]/viewQuery/{queryId:int}")]
public async Task<IActionResult> ViewQuery(int queryId)
{
// Retrieve Data from api using HttpClient
....
return PartialView("ViewQuery", qVM); // PartialView(qVM); //View(qVM);
}
Search Query Action UPDATED
[Route("searchQuery")] // customer/searchQuery
public IActionResult SearchQuery()
{
return View();
}
Can anyone please help me how do I achieve my goal. Simple - a text box were user enters queryId. A button on click, want to pass the entered queryId, call a GET action on controller and get the response. Finally show the response below the search button. I was just trying with the above modal dialog, I prefer text and not dialog.
Try & isolate the issue.
Instead of using model.QueryId in the searchQuery.cshtml, simply hardcode any reference to "modelid" - that way at least you are eliminating the possibility that Model is null on that page. Then instead of onclick="ShowResult(#Model.QueryId)"> , hard code some known id instead of #Model.QueryId. Then debug to see if your ViewQuery action method id hit. If the method is hit, then you can take it from there.
Also, I noticed that your jquery calls may need to be modified:
Instead of: $('myModalBodyDiv').html(response); it should probably be $('#myModalBodyDiv').html(response); (the "#" is missing ..) - same for $('myModal').
You can use Partial Pages(ViewQuery page) , in your searchQuery page , you could use Ajax to call server side action with parameter ID . On server side , you can query the database with ID and return PartialView with models :
[HttpPost]
public IActionResult Students (StudentFilter filters)
{
List students = Student.GetStudents(filters);
return PartialView("_Students", students);
}
Then in success callback function of Ajax , you can load the html of partial view to related area in page using jQuery :
success: function (result) {
$("#searchResultsGrid").html(result);
}
You can click here and here for code sample if using MVC template . And here is code sample if using Razor Pages .

Error when <input type="file"> is blank on a submitted form (Google App Script)

I'm creating a Google Web App (which is a HTML form) that will upload a file to a folder on My Drive. It's not required to have a file to upload, so there will be times where this input will essentially be "blank". The app works perfectly fine, except when you don't choose a file to upload. It spits out this error: "Exception: We're sorry, a server error occurred. Please wait a bit and try again." I have two files, the html file and the .gs file. Here's they are:
/* The script is deployed as a web app and renders the form */
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('FormFrontend.html');
}
/* This function will process the submitted form */
function uploadFiles(form) {
try {
/* Name of the Drive folder where the files should be saved */
var dropfolder = "Uploaded Files";
var folder, folders = DriveApp.getFoldersByName(dropfolder);
/* Find the folder, create the folder if it does not exist */
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = DriveApp.createFolder(dropfolder);
}
/* Get the file uploaded though the form as a blob */
var blob = form.myFile;
var file = folder.createFile(blob);
var urlstr = file.getUrl()
/* Set the file description as the name of the uploader */
file.setDescription("Uploaded by " + form.ContactName);
/* Write response to spreadsheet */
var ss = SpreadsheetApp.openById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
var responses = ss.getSheetByName("Responses");
responses.appendRow([form.CompanyName, form.ContactName, form.PhoneNumber, form.Email, form.Date, form.Severity, form.Details, urlstr])
/* As long as there's no errors you should se the below text */
return "Form Submitted Successfully "
} catch (error) {
/* If there's an error, show the error message */
return error.toString();
}
}
<html>
<body>
<!-- This is the actual HTML form -->
<div id="theform">
<form id="myForm">
<p style="font-size:30px">Customer Form</p>
Company Name:
<input type="text" name="CompanyName">
<br>Contact Name:
<input type="text" name="ContactName">
<br>Phone Number:
<input type="text" name="PhoneNumber">
<br>Contact Email:
<input type="email" name="Email">
<br>Date:
<input type="date" name="Date">
<br>Overall Severity: (1 Lowest, 5 Highest)
<br>
<input type="number" name="Severity" min="1" max="5" value="1">
<br>Details:
<br>
<textarea name="Details" rows=10 cols=65></textarea>
<br>
<br>Additional File (Optional):
<input type="file" name="myFile">
<br>
<!-- The submit button. It calls the server side function uploadFiles() on click -->
<input type="submit" value="Submit" onclick="this.value='Submitting..';
google.script.run.withSuccessHandler(fileUploaded)
.uploadFiles(this.parentNode);
return false;">
</form>
</div>
<!-- Here the results of the form submission will be displayed -->
<div id="output"></div>
</body>
</html>
<!-- The function will be called after the Google Script has executed -->
<script>
function fileUploaded(status) {
document.getElementById('myForm').style.display = 'none';
document.getElementById('output').innerHTML = status;
}
</script>
If have tried putting an IF statement around the "/* Get the file uploaded though the form as a blob */" section that tells it to just set urlstr to nothing if form.myFile is blank, but it still fails (but if you choose an actual file, it still completes successfully). I haven't been able to get anything helpful to show up in the logger either.
I'm fairly new to Google App Script, so any help would be appreciated!
This is what ended up working. The .getContentType seems to always return "application/octet-stream" when it's left blank and checking to see if the returned content type is that specific one worked.
/* Get the file uploaded though the form as a blob */
var blob = form.myFile;
var contentType = blob.getContentType();
if (contentType != "application/octet-stream") {
var file = folder.createFile(blob);
var urlstr = file.getUrl();
/* Set the file description as the name of the uploader */
file.setDescription("Uploaded by " + form.ContactName);
} else {
var urlStr = "None given";
}
I'd check what is actually getting returned into the variable blob.
var blob = form.myFile;
Maybe check the type with JavaScript typeOf.
var whatsTheType = typeOf blob;
In this test function:
function testIt() {
var newTestFile = DriveApp.createFile('New Text File', 'Hello, world!');
var myBlob = newTestFile.getBlob();
var whatsTheType = typeof myBlob;
Logger.log('whatsTheType: ' + whatsTheType);
}
JavaScript typeof returns the type of the blob as an "object". If you check the typeof, and it's not an object, then the file wasn't uploaded.
Put in an if conditional check, rather than a "try/catch". The "try/catch" obviously isn't keeping the code from dying.
if (whatsTheType === "object") {
//Create the file
var file = folder.createFile(blob);
};
Alternatively, you should be able to check the value property of the file picker to return a name, if a file was uploaded. If there is no name for the uploaded file, then the user didn't use the file picker.

Populate List based on json data in Worklight Application

i am developing a Worklight application using DOJO which shows list data based on webservice response.For getting webservice response i have created an adapter,i tested the adapter and its working fine.My problem is to show this json data on another view ,also in the new view i taught of showing this data in a list(list should populate based on json data array length) so that if i click on a list it will show that details.
How to do this .Any help is appreciated.
Code.
.js
function dispData()
{
var invocationData = {
adapter : 'getSampleAdapter',
procedure : 'sample'
};
WL.Client.invokeProcedure(invocationData,{
onSuccess : Success,
onFailure : Failure,
});
}
function Success(result)
{
var httpStatusCode = result.status;
if (200 == httpStatusCode) {
dijit.registry.byId("view0").performTransition("view1", 1, "slide");
}
}
html file
<div data-dojo-type="dojox.mobile.ScrollableView" id="view0"
data-dojo-props="selected:false,scrollDir:'v'">
<div data-dojo-type="dojox.mobile.Heading"
data-dojo-props="label:'Status'"></div>
<button data-dojo-type="dojox.mobile.Button" style="margin-left: 38%; margin-top: 30%"
data-dojo-props="label:'Data Details', onClick:function(e){dispData();}" ></button>
</div>
<div data-dojo-type="dojox.mobile.ScrollableView" id="view1"
data-dojo-props="selected:false,scrollDir:'v'">
<div data-dojo-type="dojox.mobile.Heading"
data-dojo-props="label:'Data'" back="Status" moveTo="view0"></div>
----------Json Data Should display here--------
</div>
this is a technical forum. You must try to do it before you post this.
However use dojox.mobile.EdgeToEdgeStoreList.Put a json response in a global variable such as
var mydata="[ ";
mydata+="{ 'label': 'bla bla', 'moveTo':'#' }, ";
mydata+="] ";
global_var= eval (mydata);
globalStoreList.generateList(global_var);
and build a list programmatly(example)