HTML5 asynchronous file upload, uploaded stream is always invalid - javascript

I'm trying to debug an asynchronous file uploader that I built some time ago which is no longer working, I've spent already a good deal of time without success.
The stream that the server is receiving is always corrupted in fact the file (image) that I save cannot be opened.
To simplify debugging I have setup a brand new ASP.NET project, with two main files, the HTML file with the form field and the ASP.NET handler.
Despite the code here being very trivial, I'm still out of luck! :(
Any help is highly appreciated, many thanks!
<!DOCTYPE html>
<html>
<head>
<title>Upload Files using XMLHttpRequest - Minimal</title>
<script type="text/javascript">
function uploadFile() {
var fd = new FormData();
fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", "Handler1.ashx");
xhr.send(fd);
}
function uploadComplete(evt) {
/* This event is raised when the server send back a response */
alert(evt.target.responseText);
}
function uploadFailed(evt) {
alert("There was an error attempting to upload the file.");
}
function uploadCanceled(evt) {
alert("The upload has been canceled by the user or the browser dropped the connection.");
}
</script>
</head>
<body>
<form id="form1" enctype="multipart/form-data" method="post" action="Handler1.ashx">
<input type="file" name="fileToUpload" id="fileToUpload"/>
<input type="button" onclick="uploadFile()" value="Upload" />
</form>
</body>
</html>
and here is the ashx handler:
using System;
using System.Collections.Generic;
using System.Web.Extensions;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.IO;
namespace MultipleFileUploadTest
{
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Handler1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
var stream = context.Request.InputStream;
MemoryStream memoryStream;
ReadFully(stream, out memoryStream);
Byte[] ba = memoryStream.ToArray();
var path = #"C:\Users\giuseppe.JHP\Desktop\Image upload test\uploaded.gif";
using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
{
fs.Write(ba, 0, ba.Length);
}
//DEBUGGING CODE
//I'm opening the same file that was originally picked by the input form field and I'm now comparing the original file with the one received within the context stream. They always differ!
Byte[] ba2 = File.ReadAllBytes(#"C:\Users\giuseppe.JHP\Desktop\Image upload test\a.gif");
//equal evaluates always to false
bool equal = ba.Length == ba2.Length;
if (equal)
{
for (var i = 0; i < ba2.Length; i++)
{
if (ba[i] != ba2[i])
{
equal = false;
i = ba2.Length;
}
}
}
//equal is always false
//if (!equal)
//{
// throw Exception("Stream is not valid");
//}
//The code below will throw a Parameter is invalid exception
//System.Drawing.Image mediaObject = System.Drawing.Image.FromStream(memoryStream);
memoryStream.Close();
}
public static void ReadFully(Stream input, out MemoryStream ms)
{
ms = new MemoryStream();
byte[] buffer = new byte[16 * 1024];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}

In case it helped someone else, I've got the code to work, here is what it is changed:
public void ProcessRequest(HttpContext context)
{
if (context.Request.Files != null && context.Request.Files.Count > 0)
{
var file = context.Request.Files[0];
file.SaveAs(#"C:\Users\giuseppe.JHP\Desktop\Image upload test\uploaded.gif");
}
}

Related

WebMethod not being called although Success returned

I have an ASP.NET Web forms site with C# code behind called from VS2013 running under Win 10 and viewed in Google Chrome. I am trying to call a C# function from Javascript in the Default.aspx markup as shown below
<%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<textarea id="txtPaste" placeholder="Paste Image Here" style="height: 100px;"></textarea>
<img id="imgPaste" src="C:\Users\Simon\Pictures\Download via Dropbox a.jpg"/>
<asp:Button Text="Save" runat="server" OnClick="Save" />
<input id="Text1" type="text" name ="ImageData" hidden="hidden" />
<script type="text/javascript">
window.onload = function () {
document.getElementById('txtPaste').focus();
document.getElementById('txtPaste').onpaste = function (event) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
console.log(JSON.stringify(items));
var blob = null;
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") === 0) {
blob = items[i].getAsFile();
}
}
if (blob !== null) {
var reader = new FileReader();
reader.onload = function (event) {
document.getElementById("imgPaste").src = event.target.result;
document.getElementById("Text1").value = event.target.result;
PageMethods.SaveImg(event.target.result.toString(), onSuccess, onFailure);
};
reader.readAsDataURL(blob);
}
}
};
function onSuccess(result) {
alert("Success! " + result);
}
function onFailure(result) {
alert("Failed! " + result);
}
</script>
</asp:Content>
The PageMethod is defined in Default.aspx.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Web.Services;
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Save(object sender, EventArgs e)
{
string str = Request.Form["ImageData"];
SaveImg(str);
}
[WebMethod]
public static bool SaveImg(string str)
{
try
{
string imageData = str.Replace("data:image/png;base64,", "");
var bytes = Convert.FromBase64String(imageData);
string filePath = #"C:\Windows\Temp\File.jpg";
if (File.Exists(filePath)) File.Delete(filePath);
using (var imageFile = new FileStream(filePath, FileMode.Create))
{
imageFile.Write(bytes, 0, bytes.Length);
imageFile.Flush();
}
return false;
}
catch (Exception Ex)
{
return true;
}
}
}
When I click in txtPaste and paste an image, the image appears in imgPaste OK and can be downloaded as a file by clicking by the Save button to execute the SaveImg function.
I am trying to create the file only by pasting an image, without clicking the Save button by defining SaveImg as Web Method and calling PageMethods.SaveImg after filling the Image control. The call to SaveImg shows and alert as specified in the OnSuccess function, but SaveImg is not executed - breakpoints set in the function are not hit on the Paste event, although they are if the Save button is clicked. The same behaviour is shown if the web site is viewed in Firefox.
ScriptManager in the Master.aspx file has EnablePageMethods set to True.
I have tried the following to make SaveImg execute on the paste event without success:
1) Commenting out settings.AutoredirectMode in Route.Config made PageMethods.SaveImg return a Fail status.
2) Commenting one or both lines in global.asax:
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
3) Using an AJAX function as shown below. sParam was defined as event.target.result.toString() and the call replaced the PageMethods.SaveImg call
function showDetail(sParam) {
$.ajax({
type: "POST",
url: "Default.aspx/SaveImg",
data: "{'str': '" +sParam +"'}", // passing the parameter
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(retValue) {
// Do something with the return value from.Net method
}
});
Calling a C# function from Javascript in ASP.Net can be done by placing the C# function in the click event of a control and then calling the control's click event from Javascript as shown below for the above scenario:
JavaScript in page markup:
%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<textarea id="txtPaste" name="txtPaste" placeholder="Paste Image Here" style="height: 100px;"></textarea>
<img id="imgPaste" src="C:\Users\Simon\Pictures\Download via Dropbox a.jpg"/>
<asp:Button Text="Save" runat="server" OnClick="cmdSave_Click" ID="cmdSave" />
<input id="Text1" type="hidden" name ="ImageData" hidden="hidden" />
<script type="text/javascript">
window.onload = function () {
document.getElementById('txtPaste').focus();
document.getElementById('txtPaste').onpaste = function (event) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
console.log(JSON.stringify(items));
var blob = null;
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") === 0) {
blob = items[i].getAsFile();
}
}
if (blob !== null) {
var reader = new FileReader();
reader.onload = function (event) {
document.getElementById("imgPaste").src = event.target.result;
document.getElementById("Text1").value = event.target.result;
//PageMethods.SaveImg(event.target.result.toString(), onSuccess, onFailure);
document.getElementById("txtPaste").value = "Image Pasted"
document.getElementById("cmdSave").click();
};
reader.readAsDataURL(blob);
}
}
};
</script>
C# code:
protected void cmdSave_Click(object sender, EventArgs e)
{
string str = Request.Form["ImageData"];
SaveImg(str);
}
public bool SaveImg(string str)
{
try
{
string imageData = str.Replace("data:image/png;base64,", "");
var bytes = Convert.FromBase64String(imageData);
string filePath = #"C:\Windows\Temp\File.jpg";
if (File.Exists(filePath)) File.Delete(filePath);
using (var imageFile = new FileStream(filePath, FileMode.Create))
{
imageFile.Write(bytes, 0, bytes.Length);
imageFile.Flush();
}
return false;
}
catch (Exception Ex)
{
return true;
}
}

Is there a way to save a single image and to prevent user put more than an image using dropzone.js?

I'm try to upload image in database, i'm using drobzone.js
that's my controller code
[HttpGet]
public ActionResult Show(int? id)
{
string mime;
byte[] bytes = LoadImage(id.Value, out mime);
return File(bytes, mime);
}
[HttpPost]
public ActionResult Upload()
{
SuccessModel viewModel = new SuccessModel();
if (Request.Files.Count == 1)
{
var name = Request.Files[0].FileName;
var size = Request.Files[0].ContentLength;
var type = Request.Files[0].ContentType;
viewModel.Success = HandleUpload(Request.Files[0].InputStream, name, size, type);
}
return Json(viewModel);
}
private bool HandleUpload(Stream fileStream, string name, int size, string type)
{
bool handled = false;
try
{
byte[] documentBytes = new byte[fileStream.Length];
fileStream.Read(documentBytes, 0, documentBytes.Length);
Pictures databaseDocument = new Pictures
{
ProfilePicture=documentBytes,
FName=name,
Size=size,
Type=type
};
using(var contxt=new EnglisCenterEntities())
{
contxt.Pictures.Add(databaseDocument);
handled = (contxt.SaveChanges() > 0);
}
}
catch (Exception )
{
// Oops, something went wrong, handle the exception
}
return handled;
}
private byte[] LoadImage(int id, out string type)
{
byte[] fileBytes = null;
string fileType = null;
using(var contxt=new EnglisCenterEntities())
{
var databaseDocument = contxt.Pictures.FirstOrDefault(doc => doc.IdPicture == id);
if (databaseDocument != null)
{
fileBytes = databaseDocument.ProfilePicture;
fileType = databaseDocument.Type;
}
}
type = fileType;
return fileBytes;
}
and this is my script
<script type="text/javascript">
$(document).ready(function () {
$("#preview").fadeOut(15);
$("#refreshButton").click(function () {
var imageToLoad = $("#imageId").val();
if (imageToLoad.length > 0) {
$("#preview").attr("src", "/Document/Show/" + imageToLoad);
$("#preview").fadeIn();
}
});
});
and this is my view
<form action="/Document/Upload" class="dropzone" id="my-awesome-dropzone"></form>
<input type="text" name="imageId" id="imageId" />
<button type="button" id="refreshButton">Update Image</button>
<img src="/" style="display: none" id="preview" />
and it's working with multi images but i want to save single image and prevent the user put more than one image. Is there a way to save a single image and to prevent user put more than an image using dropzone.js?
Javascript is needed to limit maxFiles, see http://www.dropzonejs.com/#configuration-options and http://jsfiddle.net/P2dTF/2/ for example:
Dropzone.autoDiscover = true;
Dropzone.options.my-awesome-dropzone = {
maxFiles: 1
};
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
SuccessModel viewModel = new SuccessModel();
if (file != null)
{
viewModel.Success = HandleUpload(file);
}
return Json(viewModel);
}
Param name of file is important, dropzone binds single upload to param file (and multiple to a param array of files). Don't see why you need a fileStream though, fileStream is needed when you want to return a range of bytes for example with a Request Header (audio) for partial download, HttpPostedFileBase does the job in your case.
private bool HandleUpload(HttpPostedFileBase file)
{
bool handled = false;
try
{
byte[] documentBytes = new byte[file.ContentLength];
Pictures databaseDocument = new Pictures
{
ProfilePicture=documentBytes,
FName=file.FileName,
Size=file.ContentLength,
Type=file.ContentType
};
using(var contxt=new EnglisCenterEntities())
{
contxt.Pictures.Add(databaseDocument);
handled = (contxt.SaveChanges() > 0);
}
}
catch (Exception )
{
// Oops, something went wrong, handle the exception
}
return handled;
}

AJAX: Asynchronously posting a file to a server

I want to post a file to server asynchronously without posting the form. I have the following code:
var fileInput = document.getElementById('FileInput');
var file = fileInput.files[0];
var formData = new FormData();
formData.append('file', file, file.name);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://servername/controllername/AnalyseFile', true);
xhr.setRequestHeader('Content-Type', 'multipart/form-data');
xhr.send(formData);
However, when the method is executed on the server, the post body contains no files. The following is from ASP.NET MVC4:
[HttpPost]
public JsonResult AnalyseFile()
{
int filesCount = Request.Files.Count;
if(filesCount == 0) { throw new Exception('no files...'); }
// do stuff
}
The Files collection contains no files and I can't figure out why. Any help appreciated.
In the View, you can do:
<form>
<input name="input1" id="input1"/>
<input name="input2" id="input2"/>
<input name="input3" id="input3"/>
...
<input id="SelectedFile" name="SelectedFile" type="file"/>
</form>
And Javascript:
function AttLogic(_url, _data, _callback) {
$.ajax({
url: _url,
type: 'POST',
xhr: function () {
var myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) { }
return myXhr;
},
data: _data,
cache: !1,
success: _callback,
contentType: !1,
processData: !1
});
}
function FormDataCustom(f) {
var __frm = jQuery(f), data = new FormData(f);
$(':disabled[name]', __frm).each(function () {
data.append(this.name, $(this).val());
});
return data;
}
function SaveLogic(){
var dt = FormDataCustom(document.forms[0]);
AttLogic(yourUrl, dt, function (r) {
//do something here
});
}
In the Controller:
public ActionResult Save(parameter1,parameter1,..., List<HttpPostedFileBase> SelectedFile)
{
//do something here
}
You will need to read the MultiPartFormData from the Request.
As per this post:
Your method will look something like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace Some.Namespace
{
public class SomeController : ApiController
{
[HttpPost]
public async Task<JsonResult> AnalyseFile()
{
if (!Request.Content.IsMimeMultipartContent())
{
//If not throw an error
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider("c:\\tmp\\uploads");
// Read the MIME multipart content using the stream provider we just created.
IEnumerable<HttpContent> bodyparts = await Request.Content.ReadAsMultipartAsync(streamProvider);
// Get a dictionary of local file names from stream provider.
// The filename parameters provided in Content-Disposition header fields are the keys.
// The local file names where the files are stored are the values.
//depending on your version of .net, this might have been changed to FileData instead.
// see: https://msdn.microsoft.com/en-us/library/system.net.http.multipartformdatastreamprovider(v=vs.118).aspx
IDictionary<string, string> bodyPartFileNames = streamProvider.BodyPartFileNames;
//rest of code here
}
}
I haven't tested the above code, but it should point you in the right direction.
Also have a look at How To Accept a File POST
For a more recent article: https://code.msdn.microsoft.com/AngularJS-with-Web-API-22f62a6e

Upload file to j2ee spring, file is null

I'm trying to upload a file to my J2eE spring backend. But if I set file required then it does not recognize, so therefor i conclude that it must be the way I try to send it from the front end.
HTML definition:
<form id="filesUploadForm" action="spring/uploadFile" method="post" enctype="multipart/form-data">
<input class="std" id="file" type="file" name="choose">
</form>
Javascript: (Which is triggered from another button)
function uploadFile() {
var url = 'spring/uploadFile';
var formData = $('#filesUploadForm').serialize();
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onload = function() {
console.log('upload complete');
};
xhr.send(formData);
}
If it is relevant: here is the Java code:
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
public void uploadFile(#RequestParam(value = "file", required = false) MultipartFile multipartFile) {
String fileName = multipartFile.getOriginalFilename();
System.out.println(fileName);
}
I think you need to change the parameter name in your Java code:
#RequestParam(value = "choose", required = false)

Trying to upload a file using ajax in ASP.NET MVC

I am using ASP.NET MVC 3, I want to upload a image file using an ajax form
My Index view code is:
<% using (Ajax.BeginForm("Save","Home", new AjaxOptions() { HttpMethod = "POST", InsertionMode = InsertionMode.Replace }, new { enctype = "multipart/form-data" }))
{%>
<input type="file" /><input type ="submit" value="Submit File"/>
<% } %>
and Controller code is:
[HttpPost]
public ActionResult Save()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View("Index");
}
When I upload a file and click on the button, the ajax form is submitted, but i am getting a Request.File.Count of 0.
Default unobtrusive ajax in mvc doesn't support uploading files. You need to use hidden iframe/plugin (flash, silverlight..)/html5 or combination of those.
Some scripts that might help you:
http://valums.com/ajax-upload/
http://www.uploadify.com/
You can use the the plugins suggested by #Lukáš Novotný or else you can do the following
Create an Generic HTTP handler uploadfile.ashx
Post the data to the file(set the form action="yourpath/UploadFile.ashx"
In the handler you can read the file as
HttpPostedFile uploadedfile = context.Request.Files[0];
Here's my Action that manages the file uploads. Would work with most Ajaxy file uploaders. (I think)
public ActionResult Upload(HttpPostedFileBase uploadfile)
{
try
{
var dr405 = new DR405Service().GetDR405ById(new DR405DBContext(), DR405Profile.CurrentUser.TangiblePropertyId);
var saveLocation = Path.Combine(DR405Service.SavePath + DR405Profile.CurrentUser.TangiblePropertyId);
System.IO.Directory.CreateDirectory(saveLocation);
if ((int)uploadfile.ContentLength / 1024 <= 15000)
{
uploadfile.SaveAs(Path.Combine(saveLocation, Path.GetFileName(uploadfile.FileName)));
var file = new dr405files { TangiblePropertyId = DR405Profile.CurrentUser.TangiblePropertyId, FileName = uploadfile.FileName, UploadDate = DateTime.Now };
//dr405.dr405files.Add(file);
//c.dr405s.Add(dr405);
db.Entry(file).State = file.FileId == 0 ? EntityState.Added : EntityState.Modified;
//db.Entry(dr405).State = EntityState.Modified;
new DR405Service().Save(db);
ViewData["UploadStatus"] = String.Format("File name: {0}, {1}Kb Uploaded Successfully.", uploadfile.FileName, (int)uploadfile.ContentLength / 1024);
}
else
{
ViewData["UploadStatus"] = String.Format("File exceeds 15MB upload limit. Please reduce size and try again.", uploadfile.FileName);
}
}
catch (Exception ex)
{
ViewData.ModelState.AddModelError("_FORM", ex.ToString());
}
return View();
}

Categories