1

I have a form but the user's data is not stored anywhere, just sent in an email. Does anyone know of an easy way to allow the user to attach a file?

As of now, when the user clicks submit, jquery collects the data and sends it to 'AjaxController.cs'. (A form element is not used)

HTML

<div class="form">
    <input type="text" name="Name">
    <input type="file" name="File">
    <button>Submit</button>
</div>

JS

$(document).ready(function(){
    $('button').click(function(){
        var data = {};
        $('input').each(function(){
            data[this.name] = this.value;
        }
        $.post('/Ajax/Email', data, function(){
            alert("Email Sent");
        });
    });
}

C#

public class AjaxController : Controller
{
    public ActionResult Email()
    {
        MailMessage message = new MailMessage("from@fake.com","marketing@fake.com");

        foreach (string form_inputs in Request.Form.Keys)
        {
            String  input_name  =   form_inputs.ToString();
            String  input_value =   Request.Form[form_inputs].Trim();
            if(input_name == "File")
            {
                System.Net.Mail.Attachment attachment;
                attachment = new System.Net.Mail.Attachment(input_value); //ERROR
                message.Attachments.Add(attachment);
            }
            if (input_name == "Name")
            {
                message.Body = "Name: " + input_value;
            }
        }
        SmtpClient client = new SmtpClient();
        client.Port = 25;
        client.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
        client.UseDefaultCredentials = false;
        client.Host = "SMTP.fake.com";

        client.Send(message);
    }
}

/Ajax/Email simply returns 'Mail Sent!"

I'm getting an error that basically says the uploaded file does not exist locally - of course - because it hasn't been uploaded. Where does the file exist at this point, how do I access it?

dee-see
  • 23,668
  • 5
  • 58
  • 91
Travis Heeter
  • 13,002
  • 13
  • 87
  • 129
  • 1
    It is stored in the post data, you can read the file upload contents into a memory stream by converting it from the post data. You can then pass the stream to the overloaded constructor for Attachment: http://msdn.microsoft.com/en-us/library/ab7hb4y5(v=vs.110).aspx I'll add an answer shortly – xDaevax Sep 15 '14 at 18:30

1 Answers1

3

Files uploaded in MVC are in the Request collection as an HttpPostedFileBase type. You can use this to get a stream instance of the file in memory on the server and attach it directly to the e-mail. When using a FORM tag, you must set the the enctype="multipart/form-data" in your form.

In your controller:

public class AjaxController : Controller
{
    public ActionResult Email()
    {
        MailMessage message = new MailMessage("from@fake.com","marketing@fake.com");

        foreach (string form_inputs in Request.Form.Keys)
        {
            String  input_name  =   form_inputs.ToString();
            String  input_value =   Request.Form[form_inputs].Trim();
            if(input_name == "File")
            {            
                HttpPostedFileBase file = Request.Files[input_name];
                System.Net.Mail.Attachment attachment;
                attachment = new System.Net.Mail.Attachment(file.InputStream, file.FileName); //ERROR
                message.Attachments.Add(attachment);
            }
            if (input_name == "Name")
            {
                message.Body = "Name: " + input_value;
            }
        }
        SmtpClient client = new SmtpClient();
        client.Port = 25;
        client.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
        client.UseDefaultCredentials = false;
        client.Host = "SMTP.fake.com";

        client.Send(message);
    }
}

I also feel compelled to mention the fact that this could be VERY insecure (dangerous) depending on how you're planning to implement this. Please be sure to perform some verification on the file prior to sending it (in memory Virus scan / content type restrictions, etc...).


Because you're using Ajax (which the POST method uses under the covers), you'll have some additional challenges. Up until recently, Ajax file uploads had to be done with an iframe hack. With modern browsers that support XHR2, it can be done only using the FormData object in JavaScript. Luckily, these factors won't affect the server-side code. See this Question for more detail on this: jQuery Ajax File Upload

For more on using FormData, see here: Sending multipart/formdata with jQuery.ajax

Here is an in-depth article discussing ajax form uploads: http://abandon.ie/notebook/simple-file-uploads-using-jquery-ajax

Community
  • 1
  • 1
xDaevax
  • 2,012
  • 2
  • 25
  • 36
  • 1
    Awesome! Only thing is I'm getting an error on Request.Form[input_name]. It says "Cannot Implicitly convert type 'string' to 'System.Web.HttpPostedFileBase'" – Travis Heeter Sep 15 '14 at 18:44
  • @TravisHeeter I have edited the answer. It should reference `Request.Files`, not `Request.Form`. Also, you will need to set the `enctype="multipart/form-data"` on your form for this to work. – xDaevax Sep 15 '14 at 18:44
  • I'm not using a form element, as specified in my question. I'm doing everything with ajax. Do I have to use a form element for this to work? – Travis Heeter Sep 15 '14 at 18:55
  • I got a "JavaScript component does not have a method named: "available"' when calling method: [nsIInputStream::available]" error. I looked it up and this, http://stackoverflow.com/questions/15772920/firefox-exception-javascript-component-does-not-have-a-method-named-available, said to stringify the data. I did that, but then I got an error in AjaxController, "The name 'Form' does not exist in the current context" when I tried to access Request.Form["Input Name"]. I suspect stringifying the data is causing C# to have issues with it. I will look into it and get back. – Travis Heeter Sep 15 '14 at 19:26
  • Related: http://stackoverflow.com/questions/166221/how-can-i-upload-files-asynchronously-with-jquery – xDaevax Sep 15 '14 at 19:42
  • Unfortunately I had to move on, so I took the file upload input out, and will try again later. It seems like there is no way to achieve what I want across all browsers (because of older browsers' weariness of XHR). Because of that I'll have to use a combination of tactics or (the more probable answer) revert back to submitting the form in the old fashion, without ajax. I will mark this answer as correct, but revisit in the future when I can completely test the combination of ajax and hidden iframes. – Travis Heeter Sep 16 '14 at 12:57