Spring/Javascript: Send canvas image as a MultipartFile using AJAX - javascript

I'm trying to send my canvas image to controller. I tried using ajax but it doesn't seem to work. Hope you can help me. This is my code.
function takeSnapshot() {
var video = document.querySelector('video')
, canvas;
var img = document.querySelector('img') || document.createElement('img');
var context;
var width = video.offsetWidth
, height = video.offsetHeight;
canvas = canvas || document.createElement('canvas');
canvas.width = width;
canvas.height = height;
context = canvas.getContext('2d');
context.drawImage(video, 0, 0, width, height);
img.src = canvas.toDataURL('image/png');
document.body.appendChild(img);
var dataURL = canvas.toDataURL('image/png');
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("uploadfile", blob, "test");
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/api/file/upload",
data: fd,
processData: false, //prevent jQuery from automatically transforming the data into a query string
contentType: false,
cache: false,
success: (data) => {
alert("shit");
},
error: (e) => {
alert("error");
}
});
}
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
And this is my controller that receives the submitted image.
#PostMapping("/api/file/upload")
public String uploadMultipartFile(#RequestParam("uploadfile") MultipartFile file) {
try {
fileStorage.store(file);
return "File uploaded successfully! -> filename = " + file.getOriginalFilename();
} catch (Exception e) {
return "Error -> message = " + e.getMessage();
}
}
I tried setting the value of input file type mannually but others said that it may lead to security problems. And I also I can't make it work. I want to do direct image capturing and save the image to server. I'm using spring and hibernate. Hoping you could help me. Thank you !!

Related

How can i send Canvas Image as a HttpPostedFilebase object ? it is possible?

I am trying to send an image from my webcam directly to controller.
<div id="container">
<video autoplay="true" id="videoElement">
</video>
<canvas id="myCanvas" width="400" height="350"></canvas>
<button onclick="snapshot()">Click</button>
</div>
I am sending this
image as base64 string to my controller through ajax.
<script>
function snapshot() {
// Draws current image from the video element into the canvas
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
var imgInfo = canvas.toDataURL("image/png");
console.log(imgInfo);
var image = imgInfo; // to create a image read the previous example
$.ajax({
url: "#Url.Action("Contact","Home")",
// send the base64 post parameter
data: {
base64: image
},
// important POST method !
type: "post",
complete: function () {
console.log("Ready");
}
});
}
</script>
Right now Controller looks like..
public ActionResult Contact(string base64)
{
//
}
Is it possible that i send this base64 string as HttpPostedFileBase object to Controller ?
Like..
public ActionResult Contact(HttpPostedFileBase anyname)
{
//
}
Since you have got URI string you can create Blob and send it to controller.
<script>
function dataURItoBlob(dataURI) {
const [metaData, data] = dataURI.split(',');
const [prefix, mimeSection] = metaData.split(':');
const [mimeString, separator] = mimeSection.split(';')
const byteString = atob(data);
const arrayBuffer = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([arrayBuffer], { type: mimeString });
}
function snapshot() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
var imgInfo = canvas.toDataURL("image/png");
var blobImage = dataURItoBlob(imgInfo);
var formData = new FormData();
formData.append("image", blobImage)
$.ajax({
url: "#Url.Action("Contact", "Home")",
data: formData,
processData: false,
contentType: false,
type: "post",
complete: function () {
console.log("Ready");
}
});
}
</script>
Unfortunately you cannot use default HttpPostedFileBase model binding from function parameter. You have to use workaround below or create custom model binder out of it.
public ActionResult Contact()
{
var file = Request.Files
.Cast<string>().Select(key => Request.Files[key])
.First();
// Further logic...
}

A image converted with canvas fails to be read by python PIL _io.BytesIO

Python PIL rejects to read an image you have resized with Javascript canvas
I resize an image on the client-side with Javascript:
var reader = new FileReader();
reader.onload = function (e) {
el('image-picked').src = e.target.result;
el('image-picked').className = '';
var image = new Image();
//compress Image
image.onload=function(){
el("image-picked").src=image.src;
var canvas=document.createElement("canvas");
var context=canvas.getContext("2d");
new_size = get_sizes(image.width,image.height,max_side_px)
[canvas.width,canvas.height] = new_size;
context.drawImage(image,
0,
0,
image.width,
image.height,
0,
0,
canvas.width,
canvas.height
);
console.log("Converted");
//el('image-picked').className = 'no-display'
//el('image-picked').src=""
el('upload-Preview').className = ''
el('upload-Preview').src = canvas.toDataURL("image/png", quality);
The result seems ok, less size, seemingly ok:
There are only minor differences on the files with identify:
(base) ➜ Desktop file before.png after.png
before.png: PNG image data, 4048 x 3036, 8-bit/color RGB, non-interlaced
after.png: PNG image data, 500 x 375, 8-bit/color RGBA, non-interlaced
Then I send the file via POST:
var xhr = new XMLHttpRequest();
var loc = window.location
xhr.open('POST', `${loc.protocol}//${loc.hostname}:${loc.port}/analyze`, true);
xhr.onerror = function() {alert (xhr.responseText);}
xhr.onload = function(e) {
if (this.readyState === 4) {
var response = JSON.parse(e.target.responseText);
el('result-label').innerHTML = `Result = ${response['result']}`;
}
}
var fileData = new FormData();
var file = new File([el('upload-Preview').src],
"image.png", { type: "image/png",
lastModified : new Date()});
fileData.append('file', uploadFiles[0]);
xhr.send(fileData);
And then I read on the server with python open_image(BytesIO(img_bytes)) :
#app.route('/analyze', methods=['POST'])
async def analyze(request):
data = await request.form()
img_bytes = await (data['file'].read())
img = open_image(BytesIO(img_bytes))
The above works without problems with any normal image, but it fails with any image that is the result of the resize with js, and the error is
File "/Users/brunosan/anaconda3/envs/fastai/lib/python3.7/site-packages/PIL/Image.py", line 2705, in open
% (filename if filename else fp))
OSError: cannot identify image file <_io.BytesIO object at 0x124ce6360>```
I've tried canvas.toDataURL("image/jpeg", quality) on the JS side, and reading directly with PIL (not fastai, which calls PIL). It's the same error :frowning_face:
Found the answer here.
I was injecting the image as a a DataURL, when the POST expected a binary. I could see the difference using the "Network" tab:
To convert a DataURL into the binary we need to make a Blob, and then put it into a File:
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
blob=dataURItoBlob(el('upload-Preview').src)
var file = new File([blob],
"image.png", { type: "image/png",
lastModified : new Date()});
var fileData = new FormData();
fileData.append('file', file);

C# Bitmap to Html img using ajax doesn't work

This is my C# code to make Bitmap
public void VerificationCode(int captchaWidth = 75, int captchaHeight = 25)
{
var colorList = new List<Color> { Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Blue, Color.Brown };
Response.ContentType = "image/gif";
Response.Clear();
Response.BufferOutput = true;
var randString = new Random((int)DateTime.Now.Ticks).Next(99999).ToString("00000");
Session["VerificationCode"] = randString;
var bitmap = new Bitmap(captchaWidth, captchaHeight);
var graph = Graphics.FromImage(bitmap);
var font = new Font("Arial", 16, FontStyle.Italic);
var fontColor = Color.FromArgb(153, 153, 153);
graph.Clear(Color.White);
graph.DrawString(randString, font, new SolidBrush(fontColor), 0, 0);
var random = new Random((int)DateTime.Now.Ticks);
var randomColor = new Random((int)DateTime.Now.Ticks);
for (var i = 0; i < 100; i++)
{
var c = randomColor.Next(0, colorList.Count);
var randPixelX = random.Next(0, captchaWidth);
var randPixelY = random.Next(0, captchaHeight);
bitmap.SetPixel(randPixelX, randPixelY, colorList[c]);
}
bitmap.Save(Response.OutputStream, ImageFormat.Gif);
}
$('#ChangeCaptcha').on('click', function () {
$.ajax({
url: '#Url.Action("VerificationCode", "Base")',
type: 'Get',
async: false,
success: function (data) {
console.log(data);
$('#CaptchaImage').attr('src', "data:image/gif;base64," + data);
}
});
});
This is my Javascript code using ajax to get Bitmap to change img src.
But It's doesn't work.I alway get error message. Could any one can help me to fix this issue. I have used change minitype to "data:image/bmp;base64" or "data:image/gif," then I always get error.
You are serializing Bitmap to response stream. Your JS will receive a .net serialized object of type System.Byte[] and not a Base64 encoded string.
Save your bitmap to byte array through MemoryStream.ToArray()
Convert this array to Base64 string with Convert.ToBase64String(bitmapBytes)
Send resulting string to JS

Return base64 String of a File as a response using ASP.NET httpHandler inresponse to AJAX Request

I want to download multiple types of files using AJAX.
I decided to generate a get requests with file name as query string using AJAX and get the response as base64 string and then convert it to blob object and download/save it using hyperlink a tag along with download attribute.
Kindly help me, how to use the optimized way/code to convert file to base64 string and then revert the base64 string to blob?
ASP.Net httpHandler code is here:
Imports System
Imports System.Web
Imports System.IO
Public Class FileDownload : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim Request As HttpRequest = context.Request
Dim Response As HttpResponse = context.Response
Dim serverFile As String = Request.QueryString("filename")
Dim filePath As String = String.Empty
filePath = context.Server.MapPath("~") & "\" & serverFile
Dim file As New System.IO.FileInfo(filePath)
Try
If (file.Exists) Then
Response.Clear()
Response.ClearContent()
Using reader As New FileStream(filePath, FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
Dim buffer As Byte() = New Byte(reader.Length - 1) {}
reader.Read(buffer, 0, CInt(reader.Length))
Response.Write(Convert.ToBase64String(buffer))
End Using
Response.Flush()
Response.End()
Else
Response.Write("File Not Found!")
Response.StatusCode = 500
End If
Catch ex As Exception
Response.Write(ex.ToString)
Response.StatusCode = 500
context.ApplicationInstance.CompleteRequest()
End Try
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Javascript function called with httphadler's application path as url and client side file name as filename
function downloadFileByAjax(filename, url) {
$.ajax({
type: 'GET',
url: url,
responseType: 'arraybuffer',
downloadid: filename,
success: function (result, status, xhr) {
var octetStreamMime = 'application/octet-stream';
var filename = this.downloadid;
var contentType = xhr.getResponseHeader('content-type') || octetStreamMime;
var a = document.createElement('a');
var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
if (urlCreator && window.Blob && ('download' in a) && window.atob) {
var blob = base64ToBlob(result, contentType);
var url = window.URL.createObjectURL(blob);
a.setAttribute('href', url);
a.setAttribute("download", filename);
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(event);
//window.URL.revokeObjectURL(url);
}
},
error: function (xhr, msg, error) {
//console.log(xhr, msg, error);
//console.log(xhr.responseText);
console.log(msg);
},
complete: function (xhr, status) {
//console.log('completed');
}
});
}
function base64ToBlob(base64, mimetype, slicesize) {
if (!window.atob || !window.Uint8Array) {
console.log('The current browser doesnot have the atob function. Cannot continue');
return null;
}
mimetype = mimetype || '';
slicesize = slicesize || 512;
var bytechars = atob(base64);
var bytearrays = [];
for (var offset = 0; offset < bytechars.length; offset += slicesize) {
var slice = bytechars.slice(offset, offset + slicesize);
var bytenums = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
bytenums[i] = slice.charCodeAt(i);
}
var bytearray = new Uint8Array(bytenums);
bytearrays[bytearrays.length] = bytearray;
}
return new Blob(bytearrays, { type: mimetype });
}
To convert a file to base64 string use the following code
string data;
using (FileStream fs = new FileStream(dir + fileName, FileMode.Open, FileAccess.Read)) {
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, (int)fs.Length);
data = Convert.ToBase64String(buffer);
fs.Close();
}
return data;
In the success function of the ajax call convert the string to blob using the following code
var byteCharacters = atob(data);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var blob = null;
blob = new Blob([byteArray], { type: 'text/plain' });
blob = new Blob([byteArray], { type: 'application/pdf;base64' });
var blobUrl = URL.createObjectURL(blob);
Depending upon the file format you can specify the type attribute for the blob and then assign the blobUrl as the source to your anchor tag

Safari browsers sends blob/datauri as string in Node.js

I am trying to receive the file sent through AJAX. What's happening is that when the file sent using Chrome/Firefox the file goes to req.files but when it was sent using Safari, the file goes to req.params. The application treat the file as a string "[Object blob]". Thanks.
Sending userdata through ajax.
updatePartnerProfile: function(obj){
var parentObj = this;
var target = $(obj.target);
var parent = target.closest('#editPartnerDetailsForm');
var logoImg = parent.find('.cropped');
var companyLogoBase64 = logoImg.find('.croppedImage').attr('src');
var companyLogo = util.dataURItoBlob(companyLogoBase64);
var userData = new FormData();
userData.append('token', parentObj.token);
userData.append('companyLogo', companyLogo);
$.ajax({
type: 'POST',
url: parentObj.serverUrl + 'api/admin/update/organization/' + parentObj.partnerId,
processData: false,
contentType: false,
cache: false,
data: userData,
success: function(data){
//todo
}
},
error: function(err){
console.log(err);
}
});
},
dataURItoBlob : function(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
if (!_.isUndefined(dataURI)){
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
} else {
return null;
}
}
Am I missing something in the code?
Well, as per this question, you need to be using the buffer property of ia, not just [ia]. Instead of
return new Blob([ia], {type:mimeString});
try
return new Blob([ia.buffer], {type:mimeString});

Categories