Upload Picture to Windows Azure Web Site - asp.net-mvc

I have an ASP.NET MVC 4 app that i want to deploy to Windows Azure. A part of this app involves uploading a picture. When the picture is uploaded, I want to store the picture in a directory located at /pictures/uploaded.
My question is, how do I upload a picture to a relative path within my app hosted on Windows Azure? Up to this point, my app has been hosted in a Virtual Machine. I was able to do the above by using the following:
string path = ConfigurationManager.AppSettings["rootWebDirectory"] + "/pictures/uploaded;
// Get the file path
if (Directory.Exists(path) == false)
Directory.CreateDirectory(path);
string filePath = path + "/uploaded" + DateTime.UtcNow.Milliseconds + ".png";
filePath = filePath.Replace("/", "\\").Replace("\\\\", "\\");
// Write the picture to the file system
byte[] bytes = GetPictureBytes();
using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
{
fileStream.Write(bytes, 0, bytes.Length);
fileStream.Flush();
fileStream.Close();
}
Currently, ConfigurationManager.AppSettings["rootWebDirectory"] points to an absolute path. I belive this is where my problem lies. I can't figure out how to switch all of this to a relative path.
Thank you!

Here is my as easy as it comes description of how to set this up in azure.
http://geekswithblogs.net/MagnusKarlsson/archive/2012/12/02/how-to-use-azure-storage-for-images.aspx
//Edit; heres the complete example from my blog(if the blog dies).
yourViewName.cshtml
#model List<string>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<form action="#Url.Action("Upload")" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file1" />
<br />
<label for="file">Filename:</label>
<input type="file" name="file" id="file2" />
<br />
<label for="file">Filename:</label>
<input type="file" name="file" id="file3" />
<br />
<label for="file">Filename:</label>
<input type="file" name="file" id="file4" />
<br />
<input type="submit" value="Submit" />
</form>
#foreach (var item in Model) {
<img src="#item" alt="Alternate text"/>
}
Your controller action
public ActionResult Upload(IEnumerable<HttpPostedFileBase> file)
{
BlobHandler bh = new BlobHandler("containername");
bh.Upload(file);
var blobUris=bh.GetBlobs();
return RedirectToAction("Index",blobUris);
}
Your model
public class BlobHandler
{
// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
CloudConfigurationManager.GetSetting("StorageConnectionString"));
private string imageDirecoryUrl;
/// <summary>
/// Receives the users Id for where the pictures are and creates
/// a blob storage with that name if it does not exist.
/// </summary>
/// <param name="imageDirecoryUrl"></param>
public BlobHandler(string imageDirecoryUrl)
{
this.imageDirecoryUrl = imageDirecoryUrl;
// Create the blob client.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Retrieve a reference to a container.
CloudBlobContainer container = blobClient.GetContainerReference(imageDirecoryUrl);
// Create the container if it doesn't already exist.
container.CreateIfNotExists();
//Make available to everyone
container.SetPermissions(
new BlobContainerPermissions
{
PublicAccess = BlobContainerPublicAccessType.Blob
});
}
public void Upload(IEnumerable<HttpPostedFileBase> file)
{
// Create the blob client.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Retrieve a reference to a container.
CloudBlobContainer container = blobClient.GetContainerReference(imageDirecoryUrl);
if (file != null)
{
foreach (var f in file)
{
if (f != null)
{
CloudBlockBlob blockBlob = container.GetBlockBlobReference(f.FileName);
blockBlob.UploadFromStream(f.InputStream);
}
}
}
}
public List<string> GetBlobs()
{
// Create the blob client.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Retrieve reference to a previously created container.
CloudBlobContainer container = blobClient.GetContainerReference(imageDirecoryUrl);
List<string> blobs = new List<string>();
// Loop over blobs within the container and output the URI to each of them
foreach (var blobItem in container.ListBlobs())
blobs.Add(blobItem.Uri.ToString());
return blobs;
}
}

Data and images are not meant to be stored in website directory. That's why there is the Azure Blob Storage.
Azure works by copying the website out to an instance, so if more than one instance exists then uploaded images (which are stored local to the instance) will become out of sync, you will even loose images if there are conflicts.
If you really want to do it the line below will give you what you want:
string path = Server.MapPath("~/pictures/uploaded");

There are some good answers here already that tell you exactly how to do what you need. Allow me to propose an alternative if you don't mind.
Personally, to solve problems such as yours, I turn to Azure Blob Storage. Blob storage is an extremely cheap and fast method of storing binary files (in a folder type structure) completely separate from the VM your cloud service is currently running on.
This will allow you some additional freedom when migrating or developing against your existing application since you don't have to migrate your uploads along with each deployment. Blob storage files are also triple-replicated across multiple Azure datacenters at no additional cost to you and are much more fault tolerant than your deployment VM.
Moving to Blob Storage will also allow you to access your files when your site is offline or your VM is down.
You are no longer operating in the realm of shared computing where all resources must exist on the same machine. Azure was built for scalability and separation of resources. Blob storage was designed specifically for what you are trying to do here.

string path = HostingEnvironment.MapPath(#"~/pictures/uploaded");

Related

Select folder in ASP.NET MVC application to upload files

If I want upload a file it's simply:
Razor
#Html.TextBoxFor(m => m.File, new { type = "file" })
or
Html
<input type="file">
But, If I want select a folder to upload all files than contains, is it possible?
Something like:
Razor
#Html.TextBoxFor(m => m.Folder, new { type = "folder" })
Showing a "select folder dialog".
You can't do that when using #Html.TextBoxFor. There may be another way to upload a folder, but I don't know of it.
However, you can change the file upload dialog to upload multiple files at once, which is essentially the same thing. Here is a tutorial showing how.
Essentially, you need to add the following attribute to your file upload input:
#Html.TextBoxFor(m => m.File, new { type = "file", #multiple = "multiple" })
You also need to add the multpart/form-data attribute to your Razor code to generate the form. Here is an example from that tutorial:
#using (Html.BeginForm("UploadFiles", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
At this point, you can select multiple files to upload. You can easily open the folder, hit CTRL+A to select all, then hit upload to upload the entire folder at once.
Please note that you'll need to modify the controller action to accept multiple files at once.
EDIT: Here is an example on how to handle this in your controller action, from the previously mentioned tutorial. Note that your parameter is an array of HttpPostedFileBase objects, each one representing a file you upload. Then, you iterate through each and save it.
[HttpPost]
public ActionResult UploadFiles(HttpPostedFileBase[] files)
{
//Ensure model state is valid
if (ModelState.IsValid)
{ //iterating through multiple file collection
foreach (HttpPostedFileBase file in files)
{
//Checking file is available to save.
if (file != null)
{
var InputFileName = Path.GetFileName(file.FileName);
var ServerSavePath = Path.Combine(Server.MapPath("~/UploadedFiles/") + InputFileName);
//Save file to server folder
file.SaveAs(ServerSavePath);
}
}
}
return View();
}

images not showing after website deployed to IIS

I have img tag in my view as below
<img src="#Url.Action("Images", "MemberImage", new { image = TempData["id"]+".jpg" }) "
alt=" " style="width: 100px;height:150px" />
My controller is
public ActionResult Images(string image)
{
var root = #"\\server1\PHOTOGRAPHS received\photos";
var path = Path.Combine(root, image);
path = Path.GetFullPath(path);
if (!path.StartsWith(root))
{
// Ensure that we are serving file only inside the root folder
// and block requests outside like "../web.config"
throw new HttpException(403, "Forbidden");
}
return File(path, "image/jpeg");
}
This is working perfectly in my local system but when i deployed to IIS the picture is not displaying.
The server where IIS installed has access to this path
Any other configuration i need to do?
The server may have permission to the shared directory, but it would be worth checking that the account that the website runs under (the one the app pool is using) has access to the share.

Save image using ajax in MVC 2.0

I am having this application where I need to upload images, and I am doing so using AJAX.
When I am trying to upload the image using:
<input type="submit"/>
there is no problem, but when I am trying to use,
<input type="button"/>
it is causing problem.
In my VIEW, i have something like this:
<input type="file" id="OriginalLocation" name="OriginalLocation"/>
And this is what I have put in Controller:
public ActionResult SaveEvent(EventModel viewModel, int? page)
{
int Id = Session["ID"] != null ? UtilityHelper.GetIntegerValue(Session["ID"].ToString()) : 0; //this ID we are fetching from session
HttpPostedFileBase file = Request.Files["OriginalLocation"];
viewModel.ContentType = file.ContentType;
Int32 length = file.ContentLength;
byte[] tempImage = new byte[length];
file.InputStream.Read(tempImage, 0, length);
viewModel.ActualImage = tempImage;
// BusinessLayer.Event.BusinessLayer.SetImage(viewModel);
BusinessLayer.Event.BusinessLayer.SaveEvent(viewModel, Id);
EventModel viewmodel = BusinessLayer.TeamEvent.BusinessLayer.GetAllEvents(page, Id);
return View("~/Views/Home/Event.aspx", viewmodel);
}
You cannot upload files using AJAX, at least not in older browsers that do not support the HTML 5 File API. If you do not need to support those legacy browsers you could simply use the new FormData and XMLHttpRequest objects. If on the other hand you need to support legacy browsers you might need to resort to some other techniques such as hidden iframes or Flash movies. For example there are plugins that detect the browser capabilities and based on them will use the correct technique. Take a look at the jquery.form plugin or Uploadify.

Error when Uploading image in ASP.NET MVC 3.0

On the local machine the code below works fine, but when hosted on remote server it doesn't seem to find the image file. I created the Image folder and gave it read/ write permissions as well
// --------------------------------------------
Error
Server Error in '/' Application.
Could not find file 'IdImage.jpg'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.IO.FileNotFoundException: Could not find file 'IdImage.jpg'.
Code is as below
// ---------------------------------------------
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Images/Photo"), fileName);
file.SaveAs(path);
}
return RedirectToAction("Index");
}
This looks very much like a permission problem. You said that you have created the Images folder and gave it read/write permission, but from what I can see in your code you are attempting to store the image inside a subfolder called Photo. So make sure you have created this folder as well and given read/write permissions to it as well. Also make sure that you are giving those permissions to the correct account - the one that's configured in IIS to run the application pool in which your application is hosted.
I use this code
[HttpPost]
public ActionResult Upload(System.Web.HttpPostedFileBase file)
{
string filename = Server.MapPath("~/files/somename");
file.SaveAs(filename);
return RedirectToAction("Index");
}
in view
#{
ViewBag.Title = "Upload";
}
<h2>
Upload</h2>
#using (Html.BeginForm(actionName: "Upload", controllerName: "User",
method: FormMethod.Post,
htmlAttributes: new { enctype = "multipart/form-data" }))
{
<text>Upload a photo:</text> <input type="file" name="photo" />
<input type="submit" value="Upload" />
}

Storing json in a hidden field to deserialize to a model property list

Good morning all. I've been pondering over the best way to use plupload with my mvc web app to marry up uploaded files with their respective record when created on the back-end.
I've finally boiled it down to the following process - this will work for both new and existing records (think CMS):
Create new record, use plupload to upload multiple files.
Multiple files (series of json strings representing each file) are stored on the client then posted back via hidden field.
A property on the model for the form holds the result of these uploaded files so I can handle the back-end business.
The model (without the fluff of unrelated properties) will look like this:
public class Record{
public IList<ClientFile> Files {get;set;}
}
Client file looks like this:
public class ClientFile{
public string UniqueId{get;set;}
}
My problem lies with what I have on the client after each successful upload (a json string of ClientFile) and how I can correctly get this into hidden field and bound correctly so I can have the list of client files ready in my model.
I'm thinking along the lines of either structuring the hidden field(s) right so the binder knows how to wire them up to the list, or having do some some custom deserializing myself to turn the collection of json strings into a list of objects.
Does anyone have any wisdom before I lose my bank holiday weekend to mvc.
This is how I achieve it now:
public class ProductModel
{
// This is the field bound to the view (hidden field)
public string FilesValue
{
get
{
return Files.ToJson<IEnumerable<Plupload>>();
}
set
{
Files = value.FromJson<IEnumerable<Plupload>>();
}
}
public IEnumerable<Plupload> Files { get; private set; }
}
(ToJson & FromJson are just an extension methods I use to serialise and deserialise objects quickly.)
In my view:
<input type="hidden" name="FilesValue" id="FilesValue" />
I have an inline script that converts the value of the hidden field when the form is loaded:
Bundles.AddInlineScript("fileManager.dserialize('" + Html.Raw(Model.FilesValue) + "')");
And the client script which runs before the form is posted to the server:
function preSubmit(e) {
// need to force upload
var $form = $(e.currentTarget),
hidden = $form.find('#FilesValue')[0];
if (uploader.files.length > 0) {
uploader.bind('UploadComplete', function () {
serialize(hidden);
$form.trigger('submit');
});
uploader.start();
return false;
} else {
serialize(hidden);
return true;
}
}
n.b uploader is a reference to plupload and serialize() is a local method which turns the file array into json which gets posted back.
Your client html before post should be like this:
<input type="hidden" name="Files[0].UniqueId" value"XX" />
<input type="hidden" name="Files[1].UniqueId" value"XX" />
<input type="hidden" name="Files[2].UniqueId" value"XX" />
Assuming you have a callback function for each file uploaded with a parameter containg the JSON string you can create dinamically the hiddens with jQuery after uploading each file like this:
....
var i = 0;
....
function(data){
$('form').append('<input type="hidden" name="Files[' +i+'].UniqueId" value"'
+ data.UniqueId + '" />');
i++;
}

Resources