Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Allan 42 posts 192 karma points
    Aug 13, 2018 @ 17:07
    Allan
    0

    Umbraco Forms Custom Upload Field - Store string value of file

    Wondering if anyone can help?

    I am 'rolling my own' upload field - The current upload field is not behaving well when the form is revisited (it erases all uploads if no new ones are added and can sometimes lock up files and cause issues etc.).

    I just need some guidance on saving the single string url of the uploaded file within the form - at the moment nothing gets saved!

    My view is currently just the generic -

    <input type="file" name="@Model.Name" id="@Model.Id" multiple
    @if(Model.Mandatory){<text> data-val="true" data-val-required="@Model.RequiredErrorMessage"</text>} />
    

    And my code looks like this...

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Providers.FieldTypes;
    
    public class IRFUploader : Umbraco.Forms.Core.FieldType
    {
    
        //private List<Object> _value = new List<object>();
    
        public IRFUploader()
        {
            Id = new Guid("2e41d645-0b7e-402d-aedf-a18afb168a85");
            Name = "Custom file upload";
            Description = "Renders an upload field.";
            Icon = "icon-cloud-upload ";
            DataType = FieldDataType.String;
            FieldTypeViewName = "FieldType.IRFUploader.cshtml";
        }
    
        public override IEnumerable<string> ValidateField(Form form, Field field, IEnumerable<object> postedValues, HttpContextBase context)
        {
            IList<HttpPostedFileBase> files = context.Request.Files.GetMultiple(field.Id.ToString());
    
            //foreach (HttpPostedFileBase file in files)
            //{
            //    if (file.FileName != "")
            //    {
            //        // Check something is there!!!
            //        return new[]
            //        {
            //            form.Id.ToString() + " - " + field.Id.ToString() + " - " + file.FileName // <--- YES IT IS!
    
            //        };
            //    }
            //}
    
            return Enumerable.Empty<string>();
        }
    
        public override IEnumerable<object> ProcessSubmittedValue(Field field, IEnumerable<object> postedValues, HttpContextBase context)
        {
            // HOW DO I PROCESS AND STORE THE STRING VALUE?
            return base.ProcessSubmittedValue(field, postedValues, context);
        }
    
        public override IEnumerable<object> ConvertFromRecord(Field field, IEnumerable<object> storedValues)
        {
            // HOW DO I GET THE STRING VALUE?
            return base.ConvertFromRecord(field, storedValues);
        }
    
        public override IEnumerable<object> ConvertToRecord(Field field, IEnumerable<object> postedValues, HttpContextBase context)
        {
            // HOW DO I SAVE THE STRING VALUE?
            return base.ConvertToRecord(field, postedValues, context);
        }
    
    }
    

    Any help or steer in the right direction much appreciated! :-)

  • Allan 42 posts 192 karma points
    Aug 16, 2018 @ 08:05
    Allan
    102

    Took me a few days, but I have found a solution!

    It's not the most elegant and there is one downside - Once a file is uploaded you can replace it, it will always be saved, but you cannot remove the current file completely... maybe someone has a solution to this, but for my purposes it works!

    Basically if a value exists I create a session with the key being the GUID and the value being the URL in the view...

    @using Umbraco.Web
    @model Umbraco.Forms.Mvc.Models.FieldViewModel
    
    <input type="file" name="@Model.Name" id="@Model.Id" multiple
    @if(Model.Mandatory){<text> data-val="true" data-val-required="@Model.RequiredErrorMessage"</text>} />
    
    @if (Model.Values != null && Model.Values.Any())
    {
        Session.Add(Model.Id, Model.Value); // <-- HERE!
    
        <p>
            <strong>Current File/s:</strong><br/>
            @foreach (Dictionary<string, string> dictionaryObject in Model.Values)
            {
                @*
                The model.values contains a list of dictionary objects with a single item
                With the key being the full path to the file
                And the value being the filename
                *@
                if (dictionaryObject.Count > 0)
                {
                    var fileInfo = dictionaryObject.First();
                    <a href="@fileInfo.Key" target="_blank">@fileInfo.Value</a><br/>
                    <input type="hidden" name="@(Model.Name)_file_@(fileInfo.Value)" value="@fileInfo.Key" />
                }
            }
        </p>
    }
    

    Now all I need to do is handle that value inside the 'ProcessSubmittedValue' method....

    private List<Object> myValue = new List<object>();
    
    public override IEnumerable<object> ProcessSubmittedValue(Field field, IEnumerable<object> postedValues, HttpContextBase context)
    {
        string fieldFolder = field.Id.ToString();
        string newFolder = DateTime.Now.ToUniversalTime().ToString("ddMMyyyyhhmmss");
        string dir = "/media/forms/" + fieldFolder + "/" + newFolder + "/";
    
        IList<HttpPostedFileBase> files = context.Request.Files.GetMultiple(field.Id.ToString());
    
        foreach (HttpPostedFileBase file in files)
        {
            if (file.FileName != "")
            {
                // Create directory
                if (!Directory.Exists(HttpContext.Current.Server.MapPath(dir)))
                {
                    Directory.CreateDirectory(HttpContext.Current.Server.MapPath(dir));
                }
                // Save the file
                file.SaveAs(HttpContext.Current.Server.MapPath(dir + SafeUrl(file.FileName)));
    
                // Add string to the form       
                myValue.Add(dir + SafeUrl(file.FileName));
    
                // Remove any old file
                if (context.Session[field.Id.ToString()] != null)
                {
                    string oldFile = context.Session[field.Id.ToString()].ToString();
                    string oldDir = oldFile.Substring(0, oldFile.LastIndexOf("/"));
                    // Remove old directory
                    if (Directory.Exists(HttpContext.Current.Server.MapPath(oldDir)))
                    {
                        Directory.Delete(HttpContext.Current.Server.MapPath(oldDir),true);
                    }
                }
            }
        }
        // If nothing has been added but a previous value exists in session add to the list and remove the session! 
        if (myValue.Count() == 0 && context.Session[field.Id.ToString()] != null)
        {
            myValue.Add(context.Session[field.Id.ToString()].ToString());
        }
        // Clean up session
        context.Session.Remove(field.Id.ToString());
    
        return myValue;
    
        // NOTHING TO BE DONE
        //return base.ProcessSubmittedValue(field, postedValues, context);
    }
    

    And that works for me! High five if I saved you a few days of puzzling this one out :-)

  • Allan 42 posts 192 karma points
    Aug 12, 2019 @ 14:01
    Allan
    0

    UPDATE:

    So my forms have been running for almost a year without any hitches and then lo-and-behold I get a call requesting the removal of an upload.... doh!

    I fixed the problem momentarily by commenting out the following line

    //myValue.Add(context.Session[field.Id.ToString()].ToString());
    

    This did clear things down which got me thinking...

    What if we upload a file (e.g. "delete.txt") and then checked for this filename?

    if(file.fileName != "" && file.fileName != "delete.txt"){ // CODE WILL NOT GET HERE! }
    

    Then check the string here

        if (myValue.Count() == 0 && context.Session[field.Id.ToString()] != null)
    {
        myValue.Add(context.Session[field.Id.ToString()].ToString());
    }
    

    And don't add it if the current file is called 'delete.txt'

    Just a thought...

Please Sign in or register to post replies

Write your reply to:

Draft