ASP.NET MVC - Unable to bind IFormFile to Blob (Image) - javascript

At my front-end, I am currently creating a FormData object that contains an array with the following properties: "ProductId" and "UserImage". Just to give more context, the "UserImage" property will receive a blob (image). The blob is generated with the following 2 methods: "imagetoblob" and "b64toBlob". Whenever I tried to send the FormData to my server-side, my controller will freeze for about 45 seconds and return a Network Error message. Hence, I am unable to bind the values from the FormData to my model class (Product). However, when I remove the UserImage variable from my model class, I was able to successfully send and bind the formData.
// public IFormFile UserImage { get; set; } // remove
What seems to be the problem? Below is my code and screenshots of my error
Update I am still trying to solve this issue. Any help is greatly appreciated!
Client-Side
// Assuming I have the base64URL values
var base64Image1 = "";
var base64Image2 = "";
const formData = new FormData();
formData.append("Products[0].ProductId", "1");
formData.append("Products[0].UserImage", imagetoblob(base64Image1));
formData.append("Products[1].ProductId", "1");
formData.append("Products[1].UserImage", imagetoblob(base64Image2));
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || "";
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (
var offset = 0;
offset < byteCharacters.length;
offset += sliceSize
) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, { type: contentType });
return blob;
}
function imagetoblob(base64Image) {
// Split the base64 string in data and contentType
var block = base64Image.split(";");
// Get the content type of the image
var contentType = block[0].split(":")[1]; // In this case "image/gif"
// get the real base64 content of the file
var realData = block[1].split(",")[1]; // In this case "R0lGODlhPQBEAPeoAJosM...."
// Convert it to a blob to upload
return b64toBlob(realData, contentType);
}
Server-side (Controller)
[HttpPost("verifyCart")]
public async Task<IActionResult> verifyCart([FromForm] List<Product> products)
{
}
Server-Side (Model class)
public class Product
{
public int ProductId { get; set; }
public IFormFile UserImage { get; set; }
}
FormData's UserImage key contains the blob (file) -- client side
Network Error received from client-side
Able to receive and bind FormData after removing the IFormFile UserImage at the model class --Server-side

I think your main issues are how you appending your files to your formData and the type you are using in your model.
This is what I would do if I was you:
formData.append("UserImage", imagetoblob(base64Image1));
formData.append("UserImage", imagetoblob(base64Image2));
You can append multiple files here to the same 'UserImage' key.
public class Product
{
public int ProductId { get; set; }
public List<IFormFile> UserImage { get; set; }
}
In your model use a List as your data type.
This should work for single file or multiple file uploads.

Related

Base64 encode .tgz file for use in POST upload in Javascript XHR call

I am trying to POST a .tgz file using XHR as part of a file upload.
The file itself is valid and I have tested it via manual upload. The issue I am having (I think) is when I encode the file into base64 and upload it, it is being corrupted and not not being picked up as valid.
The file itself is a plugin module for Atmail, which I have tested manually like I said.
This is my upload function with the base64 truncated.
I am encoding the target file initially with:
cat myfile.tgz | base64 > base64_file
and shortening/removing new lines with:
sed ':a;N;$!ba;s/\n/ /g' plugin.base64 > t
My question is, is this the correct way to encode a compressed file for use in my POST request? And if so what is wrong with my implementation?
function uploadPlugin()
{
var uri = "/index.php/admin/plugins/preinstall";
var name = "newPlugin";
filename = "RCE.tgz";
// Comments and extra lines removed to reduce payload size
// Remove new lines: sed ':a;N;$!ba;s/\n/ /g' plugin.base64 > t
var content = "H4sIAAAAAAAAA+0aa2/bOLJfk1/BFYJaLvyIs0m6TZpss30Awe22vabXA65XqLREx2xkSSWppNlu ...";
var formData = new FormData();
var blob = new Blob([atob(content)],
{
type: "application/x-gtar-compressed"
}
)
formData.append(name, blob, filename);
var request = new XMLHttpRequest();
request.open("POST", uri);
request.send(formData);
}
This is the ATMail plugin class I am using.
<?php
class Atmail_Test_Plugin extends Atmail_Controller_Plugin
{
protected $_pluginFullName = 'rce';
protected $_pluginModule = 'mail';
private $_loginPage = false;
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$request = $this->getRequest();
if (($request->getControllerName() == 'index' && $request->getActionName() == 'index') ||
($request->getControllerName() == 'auth' && $request->getActionName() == 'logout')) {
$this->_loginPage = true;
}
}
public function postDispatch(Zend_Controller_Request_Abstract $request)
{
if ($this->_loginPage) {
$page = $this->getResponse()->getBody();
$page = str_replace("</body>", "<!-- plugins working -->\n</body>", $page);
$this->getResponse()->setBody($page);
}
}
public function setup()
{
$db = zend_Registry::get("dbAdapter");
$db->query("drop table if exists `TestPluginSettings`");
$db->query("create table `TestPluginSettings` (`id` int auto_increment primary key, `keyName` varchar(12), `keyValue` text, index `keyName` (`keyName`))");
}
public function setViewRenderScript()
{
//return "/path/to/nothing.phtml";
}
public function setViewRenderAction()
{
}
}
I eventually found out what was going wrong. I was trying to post binary data incorrectly. Below is the working solution.
function uploadPlugin()
{
var uri = "/index.php/admin/plugins/preinstall";
var name = "newPlugin";
filename = "Upload.tgz";
var body = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xed\x1a\x6b\x6f\xdb" +
"\x38\xb2\x5f\x93\x5f\xc1\x15\x82\x5a\x2e\xfc\x88\xb3\x49\xba" +
"..." +
"...";
var formData = new FormData();
var payload = new Uint8Array(body.length);
for (var i = 0; i < payload.length; i++)
{
payload[i] = body.charCodeAt(i);
}
var blob = new Blob([payload])
formData.append(name, blob, filename);
var xhr = new XMLHttpRequest();
xhr.open("POST", uri);
xhr.send(formData);
}

File is not a constructor issue in angular 9

I am trying to crop image using the angular library "ngx-image-cropper". Thereafter cropping, I am getting an image in the base64 format so I want to convert that image to file format. I am using the following code to convert base64 string to BLOB and then BLOB to file format.
fileChangeEvent(event: any): void {
this.imageChangedEvent = event;
}
imageCropped(event: ImageCroppedEvent) {
this.croppedImage = event.base64;
var result = this.b64toBlob(this.croppedImage);
var file = new File([result], 'uploaded_file.jpg', {type: 'image/jpeg', lastModified: Date.now()});
}
b64toBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: 'image/jpeg' });
}
But on BLOB to file conversion I am getting error "ERROR TypeError: File is not a constructor". What should I do to resolve this issue?
Thanks in advance.
I was facing the same problem and I have fixed it after removing the File type from the component variable.
public imageFile: File = null;
The solution works for me:
public imageFile = null;

Encrypt parameters to JWT(JSON web token) using JS in HTML page

I want to generate JWT using JavaScript on HTML page.
I looked at Online JWT service Click Here, but can't get proper idea.
So is there any solution to generate JWT using JavaScript in HTML page.
I've encode and decode method in c# code. but i want to encode parameter from java script and will decode in c# code.
My c# Code look like:
public static string Encode(object payload, byte[] key, JwtHashAlgorithm algorithm)
{
var segments = new List<string>();
var header = new { typ = "JWT", alg = algorithm.ToString() };
byte[] headerBytes = Encoding.UTF8.GetBytes(jsonSerializer.Serialize(header));
byte[] payloadBytes = Encoding.UTF8.GetBytes(jsonSerializer.Serialize(payload));
segments.Add(Base64UrlEncode(headerBytes));
segments.Add(Base64UrlEncode(payloadBytes));
var stringToSign = string.Join(".", segments.ToArray());
var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);
byte[] signature = HashAlgorithms[algorithm](key, bytesToSign);
segments.Add(Base64UrlEncode(signature));
return string.Join(".", segments.ToArray());
}
public static string Decode(string token, byte[] key, bool verify = true)
{
var parts = token.Split('.');
var header = parts[0];
var payload = parts[1];
byte[] crypto = Base64UrlDecode(parts[2]);
var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
var headerData = jsonSerializer.Deserialize<Dictionary<string, object>>(headerJson);
var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
if (verify)
{
var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));
var keyBytes = key;
var algorithm = (string)headerData["alg"];
var signature = HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign);
var decodedCrypto = Convert.ToBase64String(crypto);
var decodedSignature = Convert.ToBase64String(signature);
if (decodedCrypto != decodedSignature)
{
throw new SignatureVerificationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature));
}
}
return payloadJson;
}
Your answer will appreciable.
Thanks,
Hardik

Pass PNG on form submit, Request URL Too long

So I have an interesting question. I have a form where a user draws an image on a canvas (think a signature pad). I then need to send the image to my C# Controller (I am using ASP.NET MVC 5). The code I have functions for shorter strings, but when I try to pass the PNG data, it is too long and I recieve a HTTP Error 414. The request URL is too long error. Here is my code:
Html:
<form id="mainForm" action="submitUserAnswer" method="post">
<input type="hidden" id="userOutput" name="output" value="" />
//...other form elements, signature box, etc.
</form>
Javascript:
function goToNextQuestion() {
var output = $('#signature').jSignature("getData");
$('#userOutput').val(output);
$('#mainForm').submit();
}
C#:
public ActionResult submitUserAnswer()
{
//use the userOutput for whatever
//submit string to the database, do trigger stuff, whatever
//go to next question
System.Collections.Specialized.NameValueCollection nvc = Request.Form;
string userOutput = nvc["output"];
ViewBag.Question = userOutput;
return RedirectToAction("redirectToIndex", new { input = userOutput });
}
public ActionResult redirectToIndex(String input)
{
ViewBag.Answer = input;
return View("Index");
}
My png data is very long, so the error makes sense. My question is how can I get the png data back to my controller?
Maybe you just need to increase allowed GET request URL length.
If that doesn't works I have an aspx WebForm that saves a signature, and I use a WebMethod
[ScriptMethod, WebMethod]
public static string saveSignature(string data)
{
/*..Code..*/
}
and I call it like this:
PageMethods.saveSignature(document.getElementById('canvas').toDataURL(), onSucess, onError);
also I have to increase the length of the JSON request and it works fine, with no problems with the lenght.
In MVC there isn't WebMethods, but JSON and AJAX requests do the job, just save the data in a session variable, and then use it when need it.
Hope it helps
You have error because your data is string (base64) and have max limit for send characters, better way is to create blob (png file) from base64 at client side, and send it to server. Edit. All listed code here, exists in stackoverflow posts.
function dataURItoBlob(dataURI) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
var byteString = atob(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
// write the bytes of the string to an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var blob = null;
// TypeError old chrome and FF
window.BlobBuilder = window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
if(window.BlobBuilder){
var bb = new BlobBuilder();
bb.append(ab);
blob = bb.getBlob(mimeString);
}else{
blob = new Blob([ab], {type : mimeString});
}
return blob;
}
function sendFileToServer(file, url, onFileSendComplete){
var formData = new FormData()
formData.append("file",file);
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onload = onFileSendComplete;
xhr.send(formData);
}
var base64 = $('#signature').jSignature("getData");
var blob = dataURItoBlob(base64);
var onComplete = function(){alert("file loaded to server");}
sendFileToServer(blob, "/server", onComplete)

Upload a file on Button click in asp

I am working on classic ASP with WinCE OS. I want to upload a file from WinCE and Save in the local machine. Please share the necessary JScript function for file upload which i can put it in a include file. Thank you.
Better way is to use any JavaScript library.. like jQuery..
Here are the file upload examples..
http://pixelcone.com/jquery/ajax-file-upload-script/
How can I upload files asynchronously?
Cheers :)
I have no information about asp classic but I have used Asp.net and you can use asp to receive file in order to upload file from wince use can develop app using c# here is an example.
Its client WinCE Application Code Function Upload(string Path, String FileName) takes File Path and File Name as Input and Post it to web page
#region Upload
public bool Upload(string FilePath, string FileName)
{
string Url = "HTTP://test.mtsonweb.com/fileupload.ashx"; // Change it to your page name
string BytesConfirmedReceived = "";
int BytesSent = 0;
bool Ret = false;
ASCIIEncoding encoding = new ASCIIEncoding();
try
{
if (File.Exists(FilePath +"\\"+ FileName) == false) { return true; }
//FileInfo oInfo = new FileInfo(FilePath + "\\" + FileName);
//BytesSent = Convert.ToInt32(oInfo.Length.ToString());
Url += "?myfile=" + FileName.Trim();
FileStream fr = new FileStream(FilePath + "\\" + FileName, FileMode.Open);
BinaryReader r = new BinaryReader(fr);
byte[] FileContents = r.ReadBytes((int)fr.Length);
BytesSent = FileContents.Length;
r.Close();
fr.Close();
WebRequest oRequest = WebRequest.Create(Url);
oRequest.Method = "POST";
oRequest.Timeout = 15000;
oRequest.ContentLength = FileContents.Length;
Stream oStream = oRequest.GetRequestStream();
BinaryWriter oWriter = new BinaryWriter(oStream);
oWriter.Write(FileContents);
oWriter.Close();
oStream.Close();
WebResponse oResponse = oRequest.GetResponse();
BytesConfirmedReceived = new StreamReader(oResponse.GetResponseStream(),
Encoding.Default).ReadToEnd();
oResponse.Close();
if (BytesSent.ToString() == BytesConfirmedReceived.Trim())
{
Ret = true;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return Ret;
}
#endregion
Now of you page you can handle file uploaded using script whatever you want, I have used asp.net with c# as back-end and below is the source of page:
<%# WebHandler Language="C#" Class="FileUpload" %>
using System;
using System.Xml;
using System.Data;
using System.Web;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
public class FileUpload : IHttpHandler
{
public void ProcessRequest(HttpContext oContext)
{
int BytesSent = 0;
//string LocalPath = #"C:\Inetpub\wwwroot\";
string MyFile = "";
try
{
MyFile = oContext.Request["myfile"].ToString().Trim();
MyFile = HttpContext.Current.Server.MapPath("~/Demo/Files/" +
ASCIIEncoding encoding = new ASCIIEncoding();
BytesSent = oContext.Request.TotalBytes;
Class1 obj = Class1.GetInstance();
obj.FileName = MyFile;
obj.FileLength = BytesSent;
byte[] InComingBinaryArray =
oContext.Request.BinaryRead(oContext.Request.TotalBytes);
obj.Data = InComingBinaryArray;
if (File.Exists(MyFile) == true)
{
File.Delete(MyFile);
}
FileStream fs = new FileStream(MyFile, FileMode.CreateNew);
BinaryWriter w = new BinaryWriter(fs);
w.Write(InComingBinaryArray);
w.Close();
fs.Close();
FileInfo oInfo = new FileInfo(MyFile);
long a = (long)BytesSent;
oContext.Response.Write(oInfo.Length.ToString());
}
catch (Exception err) { oContext.Response.Write(err.Message); }
}
public bool IsReusable { get { return true; } }
}

Categories