Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Paperclip for asp.net mvc

Is it exists kind of plugin like Paperclip for Rails?

it is really painful to implement own system for uploading files the resize it...

will be cool to have Attribute for model that will get params like so:

Model:

[Paperclip(Sizes={thumb="100x20",big="200x40"},Path="~/public/")]
public string Image{get;set;}

View:

Html.Editor(x=>x.Image)

here is small tutorial for rails.

like image 558
Sasha Avatar asked Dec 28 '22 01:12

Sasha


1 Answers

Looks like this question is pretty old, I've stumbled across it by accident and because it doesn't seem to be answered yet, I decided to bring my 2¢.

I don't know if such plugin exists for ASP.NET but it would be trivially easy to write one:

[AttributeUsage(AttributeTargets.Property)]
public class PaperClipAttribute : Attribute, IMetadataAware
{
    public PaperClipAttribute(string uploadPath, params string[] sizes)
    {
        if (string.IsNullOrEmpty(uploadPath))
        {
            throw new ArgumentException("Please specify an upload path");
        }

        UploadPath = uploadPath;
        Sizes = sizes;
    }

    public string UploadPath { get; private set; }
    public string[] Sizes { get; private set; }

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues["PaperClip"] = this;
    }
}

then define a custom model binder for the HttpPostedFileBase type:

public class PaperClipModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var paperClip = bindingContext.ModelMetadata.AdditionalValues["PaperClip"] as PaperClipAttribute;
        if (paperClip == null)
        {
            return base.BindModel(controllerContext, bindingContext);
        }

        var uploadedFile = base.BindModel(controllerContext, bindingContext) as HttpPostedFileBase;
        if (uploadedFile == null)
        {
            return null;
        }

        var uploadPath = controllerContext.HttpContext.Server.MapPath(paperClip.UploadPath);
        if (!Directory.Exists(uploadPath))
        {
            throw new ArgumentException(string.Format("The specified folder \"{0}\" does not exist", uploadPath));
        }
        var sizes =
            (from size in paperClip.Sizes
             let tokens = size.Split('x')
             select new Size(int.Parse(tokens[0]), int.Parse(tokens[1]))
            ).ToArray();

        foreach (var size in sizes)
        {
            var extension = Path.GetExtension(uploadedFile.FileName);
            var outputFilename = Path.Combine(
                uploadPath,
                Path.ChangeExtension(
                    string.Format("image{0}x{1}", size.Width, size.Height),
                    extension
                )
            );
            Resize(uploadedFile.InputStream, outputFilename, size);
        }

        return base.BindModel(controllerContext, bindingContext);
    }

    private void Resize(Stream input, string outputFile, Size size)
    {
        using (var image = Image.FromStream(input))
        using (var bmp = new Bitmap(size.Width, size.Height))
        using (var gr = Graphics.FromImage(bmp))
        {
            gr.CompositingQuality = CompositingQuality.HighSpeed;
            gr.SmoothingMode = SmoothingMode.HighSpeed;
            gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
            gr.DrawImage(image, new Rectangle(0, 0, size.Width, size.Height));
            bmp.Save(outputFile);
        }
    }
}

which will be registered in Application_Start:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    ModelBinders.Binders[typeof(HttpPostedFileBase)] = new PaperClipModelBinder();
}

and we are pretty much done. All that's left is classic stuff.

View model:

public class MyViewModel
{
    [PaperClip("~/App_Data", "100x20", "200x40", "640x480")]
    [Required(ErrorMessage = "Please select a file to upload")]
    public HttpPostedFileBase Image { get; set; }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new MyViewModel());
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }
        return Content("thanks for uploading");
    }
}

View:

@model MyViewModel

@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{ 
    @Html.LabelFor(x => x.Image)
    <input type="file" name="image" />
    @Html.ValidationMessageFor(x => x.Image)
    <input type="submit" value="Upload" />
}
like image 147
Darin Dimitrov Avatar answered Jan 17 '23 15:01

Darin Dimitrov