Base solution for your next web application
Open Closed

Upload File using DTOs #1109


User avatar
0
zokho created

Hi, Does anyone know that if its feasible to upload files using Dtos in the Application layer? If yes, can I have a sample code which shows how to define properties in DTO classes and how to call the service from AngularJS?

Thanks,


6 Answer(s)
  • User Avatar
    0
    paddyfink created

    Hello,

    I've implemented something like that to send email with attachment. this is my DTO :

    public class AttachmentDto : EntityDto<long>
        {
            public string Name { get; set; }
            public string Content { get; set; }
            public string ContentType { get; set; }
            public int ContentLength { get; set; }
        }
    

    But in order to convert my file into string, I've created a controller that receive the file, convert it into string and send the string back. Then I call my application service with the string. The controller looks like this :

    public async virtual Task<JsonResult> AddAttachment()
            {
                try
                {
                    //Check input
                    if (Request.Files.Count <= 0 || Request.Files[0] == null)
                    {
                        throw new UserFriendlyException(L("ProfilePicture_Change_Error"));
                    }
    
                    var file = Request.Files[0];
    
                    // todo : put the limit in the settings
                    if (file.ContentLength > 5242880) //Mg.
                    {
                        throw new UserFriendlyException(L("EmailAttachmentSizeLimit"));
                    }
    
                    MemoryStream target = new MemoryStream();
                    file.InputStream.CopyTo(target);
                    byte[] data = target.ToArray();
                    string base64String = System.Convert.ToBase64String(data, 0, data.Length);
    
                    //Return success
                    return Json(new MvcAjaxResponse(new AttachmentModel
                    {
                        Content = base64String,
                        ContentType=file.ContentType,
                        Name=file.FileName,
                        ContentLength=file.ContentLength
                    }));
                }
                catch (UserFriendlyException ex)
                {
                    //Return error message
                    return Json(new MvcAjaxResponse(new ErrorInfo(ex.Message)));
                }
            }
    

    I

  • User Avatar
    0
    zokho created

    Hi Thanks for your solution and sorry for the late response. Actually, I have replied but not sure where it has gone! I have got 2 questions followed by a request regarding your approach:

    1. When you say a Controller, do you mean an API within the Api project or a normal Controller and an action defined in the Web project?
    2. Why are you converting the uploaded file to String?! Do you save the converted string in DB or store in a folder?
    3. Could you please send the Angular code you use to call the Controller and the service?

    Thanks

  • User Avatar
    0
    guillaumemorin created

    We had to create a ApiController that receive the file and store it into a database table FileUpload. Then the newly created record id is returned to client. We plan to change this architecture so the file is stored somewhere else than the database, like Azure Storage.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web;
    using System.Web.Http;
    using Abp.Utils.Extensions;
    using Nito.AsyncEx.Synchronous;
    using PMP.FileUpload.Command.Remove;
    using PMP.FileUpload.Command.Upload;
    
    namespace PMP.Web.Controllers
    {
        public class FileUploadController : ApiController
        {
            private readonly IUploadService _uploadService;
            
            public FileUploadController(
                IUploadService uploadService)
            {
                _uploadService = uploadService;
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <returns>Return the list of fileId uploaded</returns>
            [HttpPost]
            public async Task<List<int>> Upload()
            {
                var output = new List<int>();
                
                // Check if the request contains multipart/form-data. 
                if (!Request.Content.IsMimeMultipartContent())
                {
                    throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
                }
    
                var streamContent = GetStreamContent();
                var streamProvider = await streamContent.ReadAsMultipartAsync();
    
                foreach (var item in streamProvider.Contents)
                {
                    if (item.Headers.ContentDisposition.Name.Contains("file"))
                    {
                        var fileName = item.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
                        var bytes = item.ReadAsByteArrayAsync().WaitAndUnwrapException();
    
                        // With IE, filename is a fullpath, so we take just the filename
                        fileName = Path.GetFileName(fileName);
    
                        var uploadOutput = _uploadService.Execute(new UploadInput()
                        {
                            FileBytes = bytes,
                            FileName = fileName
                        });
    
                        output.Add(uploadOutput.FileId);
                    }
                }
    
                return output;
            }
    
            /// <summary>
            /// ReadAsMultipartAsync() throws an exception if we do not do this. It adds a \r\n at the end of the stream.
            /// See http://stackoverflow.com/a/27050229
            /// </summary>
            /// <returns>StreamContent</returns>
            private StreamContent GetStreamContent()
            {
                Stream reqStream = Request.Content.ReadAsStreamAsync().WaitAndUnwrapException();
                MemoryStream tempStream = new MemoryStream();
                reqStream.CopyTo(tempStream);
    
                tempStream.Seek(0, SeekOrigin.End);
                StreamWriter writer = new StreamWriter(tempStream);
                writer.WriteLine();
                writer.Flush();
                tempStream.Position = 0;
    
    
                StreamContent streamContent = new StreamContent(tempStream);
                foreach (var header in Request.Content.Headers)
                {
                    streamContent.Headers.Add(header.Key, header.Value);
                }
                return streamContent;
            }
    
            
        }
    
        
    }
    
  • User Avatar
    0
    sergii created

    Hi, the proposed approach implies two submits to server:

    1. submit file only;
    2. dto with file id;

    Is it correct ? Then how about validation or other erors on second step, we have a risk to loose related file data. Maybe someone can propose other approach with single submit and server side data validation like it works for DTO's ?

  • User Avatar
    0
    gpcaretti created

    <cite>zokho: </cite> Hi, Does anyone know that if its feasible to upload files using Dtos in the Application layer? If yes, can I have a sample code which shows how to define properties in DTO classes and how to call the service from AngularJS?

    This is the same issue I have!

    I really don't know where to start. Does anybody have a working example with ABP, AngularJs page and an Application Service or an AbpApiController?

    Please, help! Gp

  • User Avatar
    0
    juslwk created

    Hi, I am creating a file storage system using .net core, here is a working example which u can test with postman this is my api controller

    public async Task<EntityDto> UploadFile([FromForm] UploadFileInput input)
            {
                File file = ObjectMapper.Map<File>(input);
                Stream stream = input.File.OpenReadStream();
                var id = await _fileManager.UploadFileToAzure(file, stream, AbpSession.GetUserId());
    
                return new EntityDto() { Id = id };
    
            }
    

    this is my DTO

    [AutoMapTo(typeof(File))]
        public class UploadFileInput 
        {
            public string Description { get; set; }
            public int ParentFolderID { get; set; }
    
            [Required]
            public IFormFile File { get; set; }
    
        }
    

    this is my file entity

    [Table("Files")]
        public class File : FullAuditedEntity, IOpenedAudited
        {
            public const int MaxNameLength = 256;
            public const int MaxDescriptionLength = 64 * 1024; //64KB
    
            [ForeignKey("UserId")]
            public User User { get; set; }
            public long UserId { get; set; }
    
            [Required]
            [MaxLength(MaxNameLength)]
            public virtual string Name { get; set; }
    
            [MaxLength(MaxDescriptionLength)]
            public virtual string Description { get; set; }
    
            public virtual long Length { get; set; }
            public virtual string ContentType { get; set; }
            public virtual string Extension { get; set; }
    
            public virtual string Uri { get; set; }
            public virtual string StoredFileName { get; set; }
    
            [ForeignKey("ParentFolderId")]
            public Folder ParentFolder { get; set; }
            public int? ParentFolderId { get; set; }
    
            public byte PublicPermissionLevel { get; set; }
    
            [ForeignKey("OwnerUserId")]
            public User OwnerUser { get; set; }
            public long OwnerUserId { get; set; }
    
            public long? LastOpenedUserId { get; set; }
            public DateTime? LastOpenedTime { get; set; }
            public bool IsTrashed { get; set; }
    
            public virtual ICollection<FilePermission> FilePermissions { get; set; }
    
            protected File()
            {
                IsTrashed = false;
                PublicPermissionLevel = (byte)PermissionLevel.None;
                this.FilePermissions = new HashSet<FilePermission>();
            }
    
        }
    

    i do custom mapping to map my file dto to my file entity

    Configuration.Modules.AbpAutoMapper().Configurators.Add(config =>
                {
                    config.CreateMap<UploadFileInput, File>()
                    .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.File.FileName))
                    .ForMember(dest => dest.ContentType, opt => opt.MapFrom(src => src.File.ContentType))
                    .ForMember(dest => dest.Length, opt => opt.MapFrom(src => src.File.Length));
    
                });
    

    and this is test with postman I put the authorization bearer token into my header, in my body i post using form data, Key Type Value
    File File Choose your file here Description Text Test Description ParentFolderID Text 1

    finally send a post request to your url eg. <a class="postlink" href="http://localhost:21021/api/services/app/FileService/UploadFile">http://localhost:21021/api/services/app ... UploadFile</a>