13

I'm using ASP.Net MVC 5 and I want to create an avatar for my user profiles. I'm not sure if what I'm doing so far is the right way, especially for security reasons so I wanted to get some advice.

What I'm doing so far

In View:

@using (Html.BeginForm("ManageUser", "Account", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    <input type="file" name="file" />
    <input type="submit" value="Add Avatar" />
}

In controller:

    internal static bool SaveAvatar(User user, HttpPostedFileBase file)
    {
        if (file == null || file.ContentLength <= 0 || file.ContentLength > MAX_LENGTH)
            return false;

        //I think I should convert the file somehow instead of saving it
        var path = HostingEnvironment.MapPath("~/img/avatar/") + string.Format("{0}.png", user.UserName);
        file.SaveAs(path);
        user.HasAvatar = true;

        return true;
    }

I have several concerns:

  • In the code above, as I commented, I think instead of just saving what user has sent to me, I should somehow use a library to convert the image to a PNG file and save it. If so, is there a good and simple library to do this?
  • I wonder whether using the user's name as a file would be good idea. After all, they choose this name and it is not something I decide.
  • Is it a good idea to use plain images or should I create a controller to validate requests or rout the requests to a hidden image?

If what I'm doing is TOTALLY wrong and you know a better source to learn the right way, please point me in the right direction. Thanks.

Community
  • 1
  • 1
Alireza Noori
  • 14,961
  • 30
  • 95
  • 179
  • 1
    The only thing I see missing, is that you should validate server side that the file is indeed an image (doing so client side doesn't sound like a good idea). See that answer [here](http://stackoverflow.com/questions/1171696/how-do-you-convert-a-httppostedfilebase-to-an-image). Beside that saving the avatar in a consistent format is a sound idea, and PNG is a good format for that, the `Image` class knows how to do that. – Simon Rapilly Sep 02 '13 at 09:55
  • 2
    Well, I would use user's Id (if you got it) instead of its UserName (file naming conventions != username conventions). Or just save the avatar in db as a table User field. You may also force users to upload only limited image types (only png, or only format you know how to convert to png) – Raphaël Althaus Sep 02 '13 at 09:56
  • @RaphaëlAlthaus I thought about user Id, but considering `User.Id` is a `Guid`, I'm not sure how good the image URL would look like. It would be something like: `site.com/img/avatar/be795f5e-ed6d-4970-aed8-7cc345ac9e9a.png` – Alireza Noori Sep 02 '13 at 10:02
  • @SimonRapilly OK. Got it. Just to be sure: `using (var image = Image.FromStream(file.InputStream, true, true))` and `image.Save(path, ImageFormat.Png);` Right? – Alireza Noori Sep 02 '13 at 10:03
  • 1
    @AlirezaNoori something like that yeah, didn't work with images in a long time, it will give you an exception if the file isn't an image, and if it is, you can check for size, format etc. – Simon Rapilly Sep 02 '13 at 10:08
  • 1
    @SimonRapilly OK. Thank you. I'm implementing what the post you mentioned says. – Alireza Noori Sep 02 '13 at 10:09
  • 1
    @AlirezaNoori , oh btw, reading the answer I linked and your problem with the file name, made me thought about the size of the image (as opposed to the size of the file), do you need to display the avatar in multiple sizes ? If so, the practice tend to be to save the avatar in multiple sizes, then load the proper size for display. Even if you need a single size, resizing the image before saving is a good option (saves space, and it avoids having to resize the avatar every-time you need to display it) – Simon Rapilly Sep 02 '13 at 10:21
  • 1
    @SimonRapilly Totally agreed. – Alireza Noori Sep 02 '13 at 10:43

2 Answers2

8

you can use this class for Upload a file to server :

public static class FileUpload
{
        public static char DirSeparator = System.IO.Path.DirectorySeparatorChar;
        public static string FilesPath = HttpContext.Current.Server.MapPath("~\\Content" + DirSeparator + "Uploads" + DirSeparator);

        public static string UploadFile(HttpPostedFileBase file)
        {
            // Check if we have a file
            if (null == file) return "";
            // Make sure the file has content
            if (!(file.ContentLength > 0)) return "";

            string fileName =DateTime.Now.Millisecond+ file.FileName;
            string fileExt = Path.GetExtension(file.FileName);

            // Make sure we were able to determine a proper extension
            if (null == fileExt) return "";

            // Check if the directory we are saving to exists
            if (!Directory.Exists(FilesPath))
            {
                // If it doesn't exist, create the directory
                Directory.CreateDirectory(FilesPath);
            }

            // Set our full path for saving
            string path = FilesPath + DirSeparator + fileName;

            // Save our file
            file.SaveAs(Path.GetFullPath(path));

            // Save our thumbnail as well
            ResizeImage(file, 70, 70);

            // Return the filename
            return fileName;
        }

        public static void DeleteFile(string fileName)
        {
            // Don't do anything if there is no name
            if (fileName.Length == 0) return;

            // Set our full path for deleting
            string path = FilesPath + DirSeparator + fileName;
            string thumbPath = FilesPath + DirSeparator + "Thumbnails" + DirSeparator + fileName;

            RemoveFile(path);
            RemoveFile(thumbPath);
        }

        private static void RemoveFile(string path)
        {
            // Check if our file exists
            if (File.Exists(Path.GetFullPath(path)))
            {
                // Delete our file
                File.Delete(Path.GetFullPath(path));
            }
        }

        public static void ResizeImage(HttpPostedFileBase file, int width, int height)
        {
            string thumbnailDirectory = String.Format(@"{0}{1}{2}", FilesPath, DirSeparator, "Thumbnails");

            // Check if the directory we are saving to exists
            if (!Directory.Exists(thumbnailDirectory))
            {
                // If it doesn't exist, create the directory
                Directory.CreateDirectory(thumbnailDirectory);
            }

            // Final path we will save our thumbnail
            string imagePath = String.Format(@"{0}{1}{2}", thumbnailDirectory, DirSeparator, file.FileName);
            // Create a stream to save the file to when we're done resizing
            FileStream stream = new FileStream(Path.GetFullPath(imagePath), FileMode.OpenOrCreate);

            // Convert our uploaded file to an image
            Image OrigImage = Image.FromStream(file.InputStream);
            // Create a new bitmap with the size of our thumbnail
            Bitmap TempBitmap = new Bitmap(width, height);

            // Create a new image that contains are quality information
            Graphics NewImage = Graphics.FromImage(TempBitmap);
            NewImage.CompositingQuality = CompositingQuality.HighQuality;
            NewImage.SmoothingMode = SmoothingMode.HighQuality;
            NewImage.InterpolationMode = InterpolationMode.HighQualityBicubic;

            // Create a rectangle and draw the image
            Rectangle imageRectangle = new Rectangle(0, 0, width, height);
            NewImage.DrawImage(OrigImage, imageRectangle);

            // Save the final file
            TempBitmap.Save(stream, OrigImage.RawFormat);

            // Clean up the resources
            NewImage.Dispose();
            TempBitmap.Dispose();
            OrigImage.Dispose();
            stream.Close();
            stream.Dispose();
        }

}

you can also save the thumbnail of their photos using ResizeImage method and In the controller I'll save file name in the database this way:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(User user, HttpPostedFileBase file)
{

                // TODO: Add insert logic here
            user.Pictuer = FileUpload.UploadFile(file);
                    db.User.Add(user);
                    db.SaveChanges();
                    return RedirectToAction("Index");


 }

also for convert uploaded images you can use this code inside UploadFile method :

    System.Drawing.Image image1 = System.Drawing.Image.FromFile(@"C:\test.bmp");
// Save the image in JPEG format.
        image1.Save(@"C:\test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

        // Save the image in GIF format.
        image1.Save(@"C:\test.gif", System.Drawing.Imaging.ImageFormat.Gif);

        // Save the image in PNG format.
        image1.Save(@"C:\test.png", System.Drawing.Imaging.ImageFormat.Png); 
Sirwan Afifi
  • 10,654
  • 14
  • 63
  • 110
0

I found this post very helpful and I wanted to share my implementation of Sirwan Afifi's excellent answer. I needed the resize function to take and return an image so it's a little different. However I've also added a boolean to preserve the aspect ratio of an image if you want.

public static Image ResizeImage(Image img, int width, int height, bool preserveAspectRatio = false)
    {

        int newWidth;
        int newHeight;

        if (preserveAspectRatio)
        {
            int originalWidth = img.Width;
            int originalHeight = img.Height;
            float percentWidth = (float)width / (float)originalWidth;
            float percentHeight = (float)height / (float)originalHeight;
            float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
            //Rounding to get the set width to the exact pixel
            newWidth = (float)originalWidth * percent < (float)width ? (int)(Math.Ceiling((float)originalWidth * percent)) : (int)((float)originalWidth * percent);
            //Rounding to get the set height to the exact pixel
            newHeight = (float)originalHeight * percent < (float)height ? (int)(Math.Ceiling((float)originalHeight * percent)) : (int)((float)originalHeight * percent);
        }
        else
        {
            newWidth = width;
            newHeight = height;
        }

        // Create a new bitmap with the size 
        Bitmap TempBitmap = new Bitmap(newWidth, newHeight);

        // Create a new image that contains are quality information
        Graphics NewImage = Graphics.FromImage(TempBitmap);
        NewImage.CompositingQuality = CompositingQuality.HighQuality;
        NewImage.SmoothingMode = SmoothingMode.HighQuality;
        NewImage.InterpolationMode = InterpolationMode.HighQualityBicubic;



        // Create a rectangle and draw the image
        Rectangle imageRectangle = new Rectangle(0, 0, newWidth, newHeight);
        NewImage.DrawImage(img, imageRectangle);

        return TempBitmap;

    }
DasAmigo
  • 333
  • 3
  • 12