9

I want to upload file using a WebApi by making an ajax call and the file will save into database. I tried the code given in the this link. Here, it is saved the received data to hard drive as a file with no extension specified but I want to do something like when I'm saving the file to Database I also want to save the file name and the extension cause later if I need to download the file I can provide the file name and extension with it. And in the link the file is saved to hard drive as a file but is there any way that I can directly save the file to DB.

Community
  • 1
  • 1
Rahul Chakrabarty
  • 2,149
  • 7
  • 39
  • 70
  • 2
    it's realy hard to read this message... Could you please rewrite it? Try using short sentenses. – IAfanasov Jan 12 '15 at 12:28
  • http://www.codeproject.com/Articles/806075/File-Upload-using-jQuery-AJAX-in-ASP-NET-Web-API – Greg Jan 12 '15 at 18:07

6 Answers6

18

The answer has several parts.

First, to upload the file, you can use a view with code like this:

@using (Html.BeginForm())
{
    <input type="file" value="Choose a file"/>
    <br/>
    <input type="button" value="Upload" id="upload"/>
}

@section scripts
{
<script type="text/javascript">
    $(document).ready(function() {
        $('#upload').click(function () {
            var data = new FormData();
            var file = $('form input[type=file]')[0].files[0];
            data.append('file',file);
            $.ajax({
                url: '/Api/File/Upload',
                processData: false,
                contentType: false,
                data: data,
                type: 'POST'
            }).done(function(result) {
                alert(result);
            }).fail(function(a, b, c) {
                console.log(a, b, c);
            });
        });
    });
</script>    
}

Second, to receive this data, create a controller, with a method like this:

public class FileController : ApiController
{
    [HttpPost]
    public async Task<string> Upload()
    {
       var provider = new MultipartMemoryStreamProvider();
       await Request.Content.ReadAsMultipartAsync(provider);

       // extract file name and file contents
       var fileNameParam = provider.Contents[0].Headers.ContentDisposition.Parameters
           .FirstOrDefault(p => p.Name.ToLower() == "filename");
       string fileName = (fileNameParam == null) ? "" : fileNameParam.Value.Trim('"');
       byte[] file = await provider.Contents[0].ReadAsByteArrayAsync();

       // Here you can use EF with an entity with a byte[] property, or
       // an stored procedure with a varbinary parameter to insert the
       // data into the DB

       var result 
           = string.Format("Received '{0}' with length: {1}", fileName, file.Length);
       return result;
    }
}

Third, by default the maximum upload size is limited. You can overcome this limitations modifying web.config:

  1. Add maxRequestLength="max size in bytes" in <configuration><system.web><httpRuntime>. (Or create this lement if it doesn't exist):

  2. Add maxAllowedContentLength to <configuration><system.web><security><requestFiltering><requestLimits> element (or create this element if it doesn't exist)

These entries look like this:

<configuration>
  <system.web>
    <!-- kilobytes -->
    <httpRuntime targetFramework="4.5" maxRequestLength="2000000" />

<configuration>
  <system.webServer>
   <security>
    <requestFiltering>
      <!-- bytes -->
      <requestLimits maxAllowedContentLength="2000000000"/>

NOTE: you should include this inside a <location> element, so that this limits are only applied to the particular route where the files are uploaded, like this:

<location path="Api/File/Upload">
  <system.web>
     ...
  <system.webServer>
     ...

Beware to modify the root web.config, not the one in the Views folder.

Fourth, as to saving the data in the database, if you use EF, you simply need an entity like this:

public class File
{
  public int FileId { get; set; }
  public string FileName { get; set; }
  public byte[] FileContent { get; set; }
}

Create a new object of this class, add to the context and save changes.

If you use stored procedures, create one which has a varbinary parameter, and pass the byte[] file as value.

JotaBe
  • 38,030
  • 8
  • 98
  • 117
4

A cleaner way to do this using webAPI controller is as follows:

Create a web api controller file: UploadFileController.cs

public class UploadFileController : ApiController
{
    // POST api/<controller>
    public HttpResponseMessage Post()
    {
        HttpResponseMessage result = null;
        var httpRequest = HttpContext.Current.Request;
        if (httpRequest.Files.Count > 0)
        {
            var docfiles = new List<string>();
            foreach (string file in httpRequest.Files)
            {
                var postedFile = httpRequest.Files[file];
                int hasheddate = DateTime.Now.GetHashCode();
                //Good to use an updated name always, since many can use the same file name to upload.
                string changed_name = hasheddate.ToString() + "_" + postedFile.FileName;

                var filePath = HttpContext.Current.Server.MapPath("~/Images/" + changed_name);
                postedFile.SaveAs(filePath); // save the file to a folder "Images" in the root of your app

                changed_name = @"~\Images\" + changed_name; //store this complete path to database
                docfiles.Add(changed_name);

            }
            result = Request.CreateResponse(HttpStatusCode.Created, docfiles);
        }
        else
        {
            result = Request.CreateResponse(HttpStatusCode.BadRequest);
        }

        return result;
    }
}

To use this webAPI in your markup. Use following:

<input type="hidden" id="insertPicture" />
<input id="insertFileupload" type="file" name="files[]" accept="image/*" data-url="/api/uploadfile" multiple>
<script>
 $(function () {
 $('#insertFileupload').fileupload({
   add: function (e, data) {
     var jqXHR = data.submit()
       .success(function (result, textStatus, jqXHR) {/* ... */
          $('#insertPicture').val(result);
          alert("File Uploaded");
       })
       .error(function (jqXHR, textStatus, errorThrown) {/* ... */
          alert(errorThrown);
       })
    }
  });
});

You can change the type of file (extensions to accept) in the "accept" attribute of the input tag. Hope it will help! Enjoy!

vohrahul
  • 1,123
  • 10
  • 17
  • 1
    Why do you consider this cleaner? You are using HttpContext.Current and looking for a specific server variable via string. And why are you getting a hashed DateTime.Now and taking first 4 of that. Seems expensive way to generate a name. – MPavlak Jan 29 '16 at 04:04
  • @MPavlak Both of the two things were specific to a requirement in my code. I agree, these should/not be used by everyone. I am updating my answer. Thanks for pointing. – vohrahul Apr 20 '16 at 05:56
0

You can't directly save file to the database.

One of the options, apart from saving the file locally, is saving it into the memory stream and then passing it to the database. This question can give you the code example of how you can get the filename and extension and save the file into the memory: Web API: how to access multipart form values when using MultipartMemoryStreamProvider?

Community
  • 1
  • 1
Vsevolod Goloviznin
  • 12,074
  • 1
  • 49
  • 50
0

I think what you want to achieve here is partly answered in this previous question

Now, about saving directly to database, you should be able to achieve this without saving the file to a hard drive first, normally by taking the stream byte array and putting it into your database entity or row property as a byte[] (array of bytes)

Community
  • 1
  • 1
Bishoy
  • 3,915
  • 29
  • 37
0

If you want to save the file to a BLOB field in your database, then you can use the code provided in the following post: Saving any file to in the database, just convert it to a byte array?.

The relevant code is below:

public static int databaseFilePut(MemoryStream fileToPut)
{
    int varID = 0;
    byte[] file = fileToPut.ToArray();
    const string preparedCommand = @"
                    INSERT INTO [dbo].[Raporty]
                               ([RaportPlik])
                         VALUES
                               (@File)
                        SELECT [RaportID] FROM [dbo].[Raporty]
            WHERE [RaportID] = SCOPE_IDENTITY()
                    ";
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
    using (var sqlWrite = new SqlCommand(preparedCommand, varConnection))
    {
        sqlWrite.Parameters.Add("@File", SqlDbType.VarBinary, file.Length).Value = file;

        using (var sqlWriteQuery = sqlWrite.ExecuteReader())
            while (sqlWriteQuery != null && sqlWriteQuery.Read())
                varID = sqlWriteQuery["RaportID"] is int ? (int) sqlWriteQuery["RaportID"] : 0;
    }
    return varID;
}

You can combine the approach in the link you sent with the code for committing a MemoryStream to the database provided in an answer I linked to.

You will need a specific column in your table to save the actual file name. Basically, you will need a BLOB column for the file content and another TEXT or VARCHAR column for the file name. The link you provided shows a way to obtain the name of the file.

However, as others have pointed out, you should not save files to the database. The most common way of handling file uploads is to save them to some location on the server and commit the path to the saved file to a TEXT or VARCHAR field in the database.

Community
  • 1
  • 1
Igor Ševo
  • 5,459
  • 3
  • 35
  • 80
0

js code as below.

var Sendmodel = new FormData();Sendmodel.append("TemplatePath",$('#fileTemplatePath')[0].files[0]);Sendmodel.append("Name","pradip");

        $.ajax({
            url: "api/project/SaveCertificateSettings",
            type: 'POST',               
            contentType: false,                
            processData: false,
            data: Sendmodel,
            success: function (data, textStatus, xhr) {

            },
            error: function (xhr, textStatus, errorThrown) {
                alert('error');
            }
        });

WEb api code as below.

public object SaveCertificateSettings()
    {
        string Name = Convert.ToString(HttpContext.Current.Request.Form["Name"]);
        if (HttpContext.Current.Request.Files.AllKeys.Any())
        {
            // Get the uploaded image from the Files collection
            var httpPostedFile = HttpContext.Current.Request.Files["TemplatePath"];

            if (httpPostedFile != null)
            {
                // httpPostedFile.FileName;
                // Get the complete file path

            }
        }


    }
PradipPatel1411
  • 109
  • 1
  • 3