How to add custom data to uploaded files

Hi,
I'm trying to use the FileManager component in Blazor server/client. 

I need to add a custom parameter to the data that is sent in the ajax request when an operation, such as created folder or upload file, is performed.

I've added a method binded to the OnSend event, and the code works for every type of operation except upload.

 public void onBeforeSend(BeforeSendEventArgs e)
    {
       
        string AjaxSettingsString = JsonConvert.SerializeObject(e.AjaxSettings);
        Dictionary<string, dynamic> AjaxSettings = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(AjaxSettingsString);
        string dataString = AjaxSettings["data"];

        if(e.Action != "Upload")
        {
            Dictionary<string, dynamic> data = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(dataString);

            // Add our value
            data.Add("DossierId", dos.Id);
            string modifiedDataString = JsonConvert.SerializeObject(data);
            AjaxSettings["data"] = modifiedDataString;
            string returnString = JsonConvert.SerializeObject(AjaxSettings);
            e.AjaxSettings = JsonConvert.DeserializeObject<object>(returnString);
        }
  }

When e.Action is "Upload", the Deserialization to a Dictionary does not work, as AjaxSettingsString seems to contain an array.
I've tried multiple things, such as deserializing AjaxSettingsString to a List of dictionaries and then adding my own data, but in the controller section I don't receive the data.

On the controller side, the upload action is handled by:
public async Task<IActionResult> HandleUploadOp(string path, IList<IFormFile> uploadFiles, string action, string dossierid)

Every parameter is populated correctly, except my own dossierid.

Could you please provide a working example on how to add custom data for the uploaded files? Or maybe explain how I should correctly deserialize the AjaxSettings for that case.

Thank you for your support. 

14 Replies 1 reply marked as answer

SP Sowmiya Padmanaban Syncfusion Team January 25, 2021 06:25 AM UTC

Hi Luca,  
 
Greetings from Syncfusion support. 
 
We have checked your reported issue with FileManager component. Currently, we have not provided support to pass the additional data in the Ajax request in the events of File Manager. We have considered your requirement as a feature request in SF Blazor File Manager. We will include the support for this feature in the Volume 4 release which is expected to be rolled out by the end of December 2020. 
 
Please, track the following feedback link to know the status of this feature. 
 
https://www.syncfusion.com/feedback/21122/support-for-passing-additional-parameter-on-onsend-event-of-filemanager-component
 
We appreciate your patience. 
 
Regards,  
Sowmiya.P 


Marked as answer

SP Sowmiya Padmanaban Syncfusion Team January 25, 2021 10:51 AM UTC

Hi Luca, 
 
Thanks for your patience.  
 
We are glad to announce that our patch release (V18.4.34) is rolled out successfully. In this release, we have included feature for “passing additional data in onSend event for FileManager component”. To access this feature, we suggest you to update the package to the latest version (V18.4.34).  
 
Note: 
 
Please note that we have introduced some API breaking changes in this release(v18.4.30). We would like you to review the breaking changes from the following location before you upgrade. Also, we have updated our documentation to the latest version(v18.4.30). 
 
 
  
To achieve your requirement, you need to add the data with CustomData argument in OnSend event.  
  
@using Syncfusion.Blazor.FileManager;  
@using Newtonsoft.Json    
<div class="control-section">  
    <SfFileManager TValue="FileManagerDirectoryContent">  
      <FileManagerEvents TValue="FileManagerDirectoryContent" OnSend="send"></FileManagerEvents>  
        <FileManagerAjaxSettings Url="/api/Home/FileOperations"  
                                 UploadUrl="/api/Home/Upload"  
                                 DownloadUrl="/api/Home/Download"  
                                 GetImageUrl="/api/Home/GetImage">  
        </FileManagerAjaxSettings>  
    </SfFileManager>  
</div>  
@code{  
    public void send(Syncfusion.Blazor.FileManager.BeforeSendEventArgs args)  
    {  
      Dictionary<string, object> data = new Dictionary<string, object>();  
      data.Add("FilePath", "Syncfusion");  
      args.CustomData = data;  
    }  
 
  
Controller:  
  
  public class FileManagerDirectoryContent1  
  {  
    public Dictionary<string, object> CustomData { get; set; }  
    public bool HasChild { get; set; }  
    public DateTime DateCreated { get; set; }  
    public DateTime DateModified { get; set; }  
    public string PreviousName { get; set; }  
  }  
[Route("FileOperations")]  
    public object FileOperations([FromBody] FileManagerDirectoryContent1 args)  
    {  
      string filepath = args.CustomData["FilePath"].ToString();     
 
[Route("Upload")] 
    public IActionResult Upload(string path, IList<IFormFile> uploadFiles, string action, string CustomData) 
    { 
      Dictionary<string, object> AjaxSettings = JsonConvert.DeserializeObject<Dictionary<string, object>>(CustomData); 
      var filepath = AjaxSettings["FilePath"]; 
   } 
  
  
We thank you for your support and appreciate your patience in waiting for this release. Please get in touch with us if you would require any further assistance.  
  
Regards,   
Sowmiya.P   



LU Luca February 12, 2021 03:31 PM UTC

Thank you.
I've finally tried to update to the latest version of Syncfusion (18.4.0.42) and tried to implement the breaking changes of the filemanager.
I've changed my code to reflect the example you provided for the new update, but I'm getting an exception when trying to upload a file.
Here it is:

Uncaught (in promise) Error: System.NotSupportedException: The collection type 'Microsoft.AspNetCore.Http.IHeaderDictionary' on 'Microsoft.AspNetCore.Http.IFormFile.Headers' is not supported.
   at System.Text.Json.JsonClassInfo.GetElementType(:53416/dossiers/Type propertyType, Type parentType, MemberInfo memberInfo, JsonSerializerOptions options)
   at System.Text.Json.JsonClassInfo.CreateProperty(:53416/dossiers/Type declaredPropertyType, Type runtimePropertyType, Type implementedPropertyType, PropertyInfo propertyInfo, Type parentClassType, JsonConverter converter, JsonSerializerOptions options)
   at System.Text.Json.JsonClassInfo.AddProperty(:53416/dossiers/Type propertyType, PropertyInfo propertyInfo, Type classType, JsonSerializerOptions options)
   at System.Text.Json.JsonClassInfo..ctor(:53416/dossiers/Type type, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializerOptions.GetOrAddClass(:53416/dossiers/Type classType)
   at :53416/dossiers/System.Text.Json.JsonPropertyInfo.get_ElementClassInfo()
   at System.Text.Json.JsonSerializer.Write(:53416/dossiers/Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.JsonSerializer.WriteCore(:53416/dossiers/Utf8JsonWriter writer, Object value, Type type, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializer.WriteCore(:53416/dossiers/PooledByteBufferWriter output, Object value, Type type, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializer.WriteCoreString(:53416/dossiers/Object value, Type type, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializer.Serialize[TValue](:53416/dossiers/TValue value, JsonSerializerOptions options)
   at Syncfusion.Blazor.FileManager.SfFileManager`1.BeforeUpload(:53416/dossiers/BeforeUploadEventArgs args)
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(:53416/dossiers/Task task)
   at Syncfusion.Blazor.Internal.SfBaseUtils.InvokeEvent[T](:53416/dossiers/Object eventFn, T eventArgs)
   at Syncfusion.Blazor.Inputs.SfUploader.BeforeUploadEvent(:53416/dossiers/BeforeUploadEventArgs args)
    at Object.endInvokeDotNetFromJS (blazor.server.js:8)
    at e.<anonymous> (blazor.server.js:8)
    at blazor.server.js:1
    at Array.forEach (<anonymous>)
    at e.invokeClientMethod (blazor.server.js:1)
    at e.processIncomingData (blazor.server.js:1)
    at e.connection.onreceive (blazor.server.js:1)
    at WebSocket.i.onmessage (blazor.server.js:1)

I'm using .Net Core 3.1.

The code I'm using is this one:

//On before event, where I add my custom data
public void onBeforeSend(BeforeSendEventArgs e)
{
e.CustomData = new Dictionary<string, object>();
e.CustomData.Add("DossierId", DossierId);
}

TValue of the filemanager is:

 public class FileManagerCustomDirContent : FileManagerDirectoryContent
    {
        public Dictionary<string, object> CustomData { get; set; }
    }

And the controller action has this definition:
[Route("FileManager/HandleUploadOp")]
public async Task<IActionResult> HandleUploadOp(string path, IList<IFormFile> uploadFiles, string action, string CustomData)

But the breakpoint never activates in it, the exception is displayed before, when trying to create the request I suppose.
The error occurs every time, for every type of file.





SP Sowmiya Padmanaban Syncfusion Team February 15, 2021 12:10 PM UTC

Hi Luca,  
 
We have checked your reported problem with mentioned version (18.4.0.42) but the issue is not reproduced from our end. Please refer to the below video footage. 
 
 
Please, refer the sample link below. 
 
 
 
If the issue still persists, please share the below details regarding your reported problem. 
 
1.     Share the issue replicating video footage. 
2.     If possible, replicate the issue in the attached sample. 
3.     Could you please share the file provider you have used in FileManager component? 
 
Please let us know, if you need any further assistance. 
 
Regards, 
Sowmiya.P 



LU Luca February 16, 2021 01:28 PM UTC

Thank you for your reply. 
I was able to get it working by looking at your example. 
The issue was on the razor component code: you must use the predefined type FileManagerDirectoryContent as TValue, whereas you shoukd use your custom type FileManagerDirectoryContent1 in the Controller. Can you explain why? 

In any case, now I can upload files, but I've got another problem with the FileOperations method.
In my application, I use an account system and I use the AspnetCore Identity Usermanager to retrieve the logged in user information.
In the Upload action, the user and its claims are accessible and everything works perfectly, but when trying to perform an action in the Fileoperations method, if I try to access the user or its claims both are null, as if the request was performed in a different way.
The method I use to check the claims is the same between the two methods, but it works in the upload method and doesn't in the fileoperations one.

var user = httpContextAccessor?.HttpContext?.User;
var userId = Guid.Parse(user.Claims.First(a => a.Type == JwtClaimTypes.Subject).Value);

This code above works in the upload method, and doesn't in the fileoperations one, very field of the user is pretty much null.
In the previous version (18.3.x.x) both were working.

So, within the same page, using the same component and the same controller, the upload method can access the logged in user data, whereas the fileoperations method can not.

Do you have any clue on how can I fix this? I'll try to create an example for you in the meantime. 
Thank you





LU Luca February 17, 2021 01:57 PM UTC

I was able to create a sample application to replicate the issue. The sample uses .Net core 3.1 and the basic template for new Blazor server applications, just with your code added.

Here are the steps to reproduce the issue:
1) Apply migrations to create the db, or change the connection string as you prefer, just to run the application.
2) Run the application and create a new account. The sample uses the default Asp.net core identity settings, you case use something like "Testtest1-" as the password.
3) After creating a new account and clicking on the confirm url, login.
4) You should now see your account name in the header above. Use the filemanager in the index page and set a breakpoint on Homecontroller.cs, line 41.
5) As the breakpoint triggers, you can see that the actionByUser variable is null.
6) Now try to upload a new file, but before set a breakpoint on HomeController.cs on line 90.
7) When the breakpoint triggers, the actionByUser variable is not null but instead matches the logged in user.

This is the issue I'm facing in my application. In the "Upload" method, the user exists and is valid, whereas in the "Fileoperations" method it is not.
Thank you for your support.


Attachment: BlazorAppTestFilemanager_d83c9779.zip


IL Indhumathy Loganathan Syncfusion Team February 18, 2021 03:33 PM UTC

Hi Luca, 
 
Thanks for contacting Syncfusion support. 
 
Currently we are validating your reported problem in FileManager component with the provided details. We will update you further details on 22nd February, 2021. 
 
We appreciate your patience. 
 
Regards, 
Indhumathy L 



LU Luca February 24, 2021 07:02 AM UTC

Hi,
Any update on this? Thank you.


KR Keerthana Rajendran Syncfusion Team February 24, 2021 03:05 PM UTC

Hi Luca, 
 
We regret for the inconvenience.  
 
Due to complexity, we need some additional time to validate the reported issue in our end. We will update you further details within two business days on February 26, 2021.  
 
Regards, 
Keerthana. 
 



IL Indhumathy Loganathan Syncfusion Team March 1, 2021 01:26 PM UTC

Hi Luca, 
 
Thank you for your patience. 
 
We have validated your requirement in FileManager component. We have used the HttpClient to get data from server in File Manager component. We are unable to get the HttpContext in Web API method while using HttpClient. Please refer to below Github link. 
 
 
However, we can achieve your requirement using custom data in before Send event of FileManager component. Please refer to the below code snippets. 
 
Index.razor 
 
<SfFileManager TValue="FileManagerDirectoryContent"> 
    <FileManagerEvents TValue="FileManagerDirectoryContent" OnSend="send"></FileManagerEvents> 
    <FileManagerAjaxSettings Url="/api/Home/FileOperations" 
                             UploadUrl="/api/Home/Upload" 
                             DownloadUrl="/api/Home/Download" 
                             GetImageUrl="/api/Home/GetImage"> 
    </FileManagerAjaxSettings> 
</SfFileManager> 
 
@code{ 
    public async Task send(Syncfusion.Blazor.FileManager.BeforeSendEventArgs e) 
    { 
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); 
        var details = await userManager.GetUserAsync(authState.User); 
        e.CustomData = new Dictionary<string, object>(); 
        e.CustomData.Add("user", details); 
    } 
} 
 
Controller.cs 
 
public async Task<object> FileOperationsAsync([FromBody] FileManagerDirectoryContent1 args) 
{ 
            var name = args.CustomData["user"]; 
            if(name != null) 
            { 
                        //ActionByUser will return user name. 
                        var actionByUser = JsonConvert.DeserializeObject<Microsoft.AspNetCore.Identity.IdentityUser>(args.CustomData["user"].ToString()); 
            } 
 
Please find the sample from the below link. 
 
 
Follow the below steps to run the sample. 
 
1.      Open package manager console and run Update-Database. 
2.      Register your account in the sample and click on confirm email at the last. 
3.      Then login with the credentials. 
4.      Now debug in the read operation method in home controller page, you will get the user data in custom Data argument. 
 
Please let us know if you need any further assistance. 
 
Regards, 
Indhumathy L 



LU Luca March 2, 2021 08:28 AM UTC

Thank you. This seems like a valid workaround and it works, although I preferred the way it worked on the previous version.



IL Indhumathy Loganathan Syncfusion Team March 3, 2021 05:37 AM UTC

Hi Luca, 
 
Thank you for your update. 
 
In previous version, we have used ajax to communicate between client and server. Now we have used HttpClient instead of ajax to improvise the performance of File Manager component. 
 
Please let us know if you need any further assistance. 
 
Regards, 
Indhumathy L 



AK Amjad Khan replied to Sowmiya Padmanaban May 31, 2022 05:56 PM UTC

Dear Sowmiya,

The following code shows how to get custom data in the FileOperations and Upload actions (copied from above):


[Route("FileOperations")]  
    public object FileOperations([FromBody] FileManagerDirectoryContent1 args)  
    {  
      string filepath = args.CustomData["FilePath"].ToString();     
 
[Route("Upload")] 
    public IActionResult Upload(string path, IList<IFormFile> uploadFiles, string action, string CustomData) 
    { 
      Dictionary<string, object> AjaxSettings = JsonConvert.DeserializeObject<Dictionary<string, object>>(CustomData); 
      var filepath = AjaxSettings["FilePath"]; 
   } 

Can you also please post code for getting custom data in GetImage and Download actions so I can set the correct directory:

[Route("GetImage")]

        public IActionResult GetImage(FileManagerDirectoryContent args)

        {

            //how to get CustomData here??

            //Invoking GetImage operation with the required paramaters

            // path - Current path of the image file; Id - Image file id;

            return this.operation.GetImage(args.Path, args.Id, false, null, null);

        }



IL Indhumathy Loganathan Syncfusion Team June 1, 2022 01:18 PM UTC

Hi Luca,


We don’t have any direct support to pass custom value for Download and GetImage operations. Please find the answer for your queries.


For Download request:


Since there is no direct way to pass custom value, you can prevent our default download operation by setting args.Cancel as true in BeforeDownload
event. Then you can trigger the customized download operation using an interop call where you can pass custom values to server side. Check out the below code snippet.


[Index.razor]

 

<SfFileManager TValue="FileManagerDirectoryContent">  

            . . .

    <FileManagerEvents TValue="FileManagerDirectoryContent" OnSend="onsend" BeforeDownload =” beforeDownload></FileManagerEvents>  

</SfFileManager>

...

@code{  

public async Task beforeDownload(BeforeDownloadEventArgs<FileManagerDirectoryContent> args)    

{    

    args.Cancel = true;    

    DirectoryContent[] data = new DirectoryContent[]{ new DirectoryContent()    

    {    

        Name = args.Data.DownloadFileDetails[0].Name, // name of the file    

        IsFile = args.Data.DownloadFileDetails[0].IsFile, // indicates whether file or folder    

        FilterPath = args.Data.DownloadFileDetails[0].FilterPath, // path of the file/folder from root directory    

        HasChild = args.Data.DownloadFileDetails[0].HasChild, // if folder has child folder set as true else false    

        Type = args.Data.DownloadFileDetails[0].Type // empty string for folder and file type like .png for files    

    

    } };    

    DownloadUrl = https://localhost:44355/api/FileManager/Download;    

    DirectoryContent downloadData = new DirectoryContent()    

    {    

        Data = data,    

        Path = args.Data.Path,// path in which the file is located (make ensure to add the path from root directory excluding the root directory name)    

        Names = args.Data.Names// names of the files to be downloaded in the specified path    

    };    

    await jsRuntime.InvokeAsync<object>("saveFile", downloadData, DownloadUrl);    

}

}


[_Host.cshtml]

<script>    

        window.saveFile = (data, downloadUrl) => {    

            //creating the data to call download web API method    

            var i = {    

                action: "download",    

                path: data.path,    

                names: data.names,    

                data: data.data,    

                customvalue: "Pictures",    

            }    

            …    

            //appeding the dynamically created form to the document and perform form submit to perform download operation    

            a.appendChild(s),    

                document.body.appendChild(a),    

                document.forms.namedItem("downloadForm").submit(),    

                document.body.removeChild(a)    

        }    

    </script>


For GetImage request:


We are unable to add the custom header in GetImage request since the GetImage is processed using query string parameter and download processed using form element.

  

If we are going to use ajax (asynchronous request) then, each image creates individual request and its having certain time delay based on image size, network bandwidth and server respond time. We are unable to update the headers to the corresponding image tag value. So, it is not possible to implement these operations using Ajax requests. However, you can pass the custom data in the imageUrl, but this is not preferable for sensitive data sending.


<SfFileManager TValue="FileManagerDirectoryContent">

    ...

    <FileManagerEvents TValue="FileManagerDirectoryContent" OnSend="onsend" BeforeDownload="beforeDownload" BeforeImageLoad="beforeImageLoad"></FileManagerEvents>

</SfFileManager>

...

    public void beforeImageLoad(BeforeImageLoadEventArgs<FileManagerDirectoryContent> args)

    {

        args.ImageUrl = args.ImageUrl + "&SubFolder=Pictures";

    }


[HomeController.cs]

public class FileManagerDirectoryContentExtend : FileManagerDirectoryContent    

{    

    public string customvalue { get; set; }    

    public string SubFolder { get; set; }

}    

...

[Route("Download")]   

public IActionResult Download(string downloadInput)   

{   

            FileManagerDirectoryContentExtend args = JsonConvert.DeserializeObject<FileManagerDirectoryContentExtend>(downloadInput);   

            var root = args.customvalue;   

              this.operation.RootFolder(this.basePath + "\\" + this.root + "\\" + root);

[Route("GetImage")]

public IActionResult GetImage(FileManagerDirectoryContentExtend args)

{

    var root = args.SubFolder;

    this.operation.RootFolder(this.basePath + "\\" + this.root + "\\" + root);

    return this.operation.GetImage(args.Path, args.Id, false, null, null);

}


Please check the attached sample for reference.


https://www.syncfusion.com/downloads/support/directtrac/general/ze/FileManagerSample-158474967


Kindly check the sample whether it meets your requirement. Please get back to us if you need any further assistance.


Regards,

Indhumathy L


Loader.
Up arrow icon