Download Documents from DocStart

Anyone can suggest how to automatically download documents using Epicor Function Library and calling this function in Epicor REST Swagger API

I have used below code but documents are not downloading

var hAttachment = Db.XFileRef.Where(f => f.Company == Session.CompanyID && Db.XFileAttch.Any(x => x.XFileRefNum == f.XFileRefNum && x.Key1 == PackID && (x.RelatedToFile == "ShipHead" || x.RelatedToFile == "ShipDtl"))).ToList();

if(hAttachment != null)
{
    foreach(var att in hAttachment)
    {
        this.CallService<Ice.Contracts.AttachmentSvcContract>(rpt =>
        {
            Dictionary<String,String> metaData = new Dictionary<String,String>();    
            //var fileData = rpt.DocStarDownloadFile(att.XFileRefNum, ref metaData);
            if(att.DocTypeID == "DSDOC")
            {
                byte[] fileData = rpt.DocStarDownloadFile(att.XFileRefNum, ref metaData);                    
                string xFileExt = att.XFileDesc.Substring(att.XFileDesc.Length - 4);
                string fileName = Path.GetTempFileName().Replace(".tmp", xFileExt);
                File.WriteAllBytes(fileName, fileData);   
                Process.Start(fileName);
            }
            else
            {
            
            }
            
        });
    }
}

Auto Downloading ECM Attachments - ECM - Epicor User Help Forum (epiusers.help)

This seems to be a working version and references the source work also.

1 Like

I have used the same functionality but it is not working.

You are doing a Process Start… on a Function server side… that will / should never work. You are running these files on the server. Where do you expect them to be downloading to?

When I call this Epicor function using Rest Swagger UI then It should automatically download to web browser.

How would it do that based on the code you wrote?

I wrote the Process.Start(fileName)

And what does that do?

It will start downloading the document.

HOW???

The Process.Start method in C# is used to start a new process or open a file or URL using the default application associated with it in the context . system in which it was invoked. This method is part of the System.Diagnostics namespace. It can be utilized to run executables, open documents, launch websites, or execute commands.

Here’s a brief explanation of some common usages:

  1. Starting a Program:

    using System.Diagnostics;
    
    Process.Start("notepad.exe");
    

    This line of code will launch Notepad.

  2. Opening a URL in the Default Browser:

    using System.Diagnostics;
    
    Process.Start("http://www.example.com");
    

    This will open the specified URL in the default web browser.

  3. Opening a File with its Associated Program:

    using System.Diagnostics;
    
    Process.Start("example.txt");
    

    This will open the example.txt file with the default text editor.

  4. Starting a Process with Specified Arguments:

    using System.Diagnostics;
    
    ProcessStartInfo startInfo = new ProcessStartInfo("notepad.exe")
    {
        Arguments = "example.txt"
    };
    Process.Start(startInfo);
    

    This will open example.txt in Notepad.

The Process.Start method is quite versatile and provides various overloads for different use cases, allowing for both simple and more advanced process start scenarios.

2 Likes

Like data exfiltration and installing a malicious shell. :thinking:

For security reasons, the web browser won’t automatically download anything without user permission. If it’s really important then you need a program running locally like the old .NET Client (System Monitor), the Kinetic EdgeClient, or a custom program/service that you write than has access to the local machine.

1 Like

A function triggered via REST can only return JSON. You could return the file in base64, but that won’t be much use to the user. If you really want to stream to the browser, you have to create your own WebApi and return a FileContentResult

for example:

    [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
    public async override Task<ActionResult> HandleAsync([FromRoute] Guid sysRowId, CancellationToken cancellationToken = default(CancellationToken))
    {
        // Get the file
        var file = await mErpRestClientFactory.GetService<IAAttachmentFunctions>().Call_GetAttachmentAsync(new GetAttachmentInput { SysRowID = sysRowId }, cancellationToken);

        // Get the file in bytes
        var bytes = Convert.FromBase64String(file.FileBase64);

        // Get content type
        var contentType = file.Filename.GetContentType();

        // Return file
        return new CustomFileResult(bytes, contentType)
        {
            FileDownloadName = file.Filename,
            Inline = true
        };
    }
public class CustomFileResult : FileContentResult
{
    public CustomFileResult(byte[] fileContents, string contentType) : base(fileContents, contentType)
    {
    }

    /// <summary>
    /// True to display in browser if possible vs downloading the file
    /// </summary>
    public bool Inline { get; set; }

    public override Task ExecuteResultAsync(ActionContext context)
    {
        // Check for null
        ArgumentNullException.ThrowIfNull(context, nameof(context));

        // Set header value
        var contentDispositionHeaderValue = new ContentDispositionHeaderValue(Inline ? "inline" : "attachment");
        contentDispositionHeaderValue.SetHttpFileName(FileDownloadName);
        FileDownloadName = null;

        // Add header
        context.HttpContext.Response.Headers.Add(HeaderNames.ContentDisposition, contentDispositionHeaderValue.ToString());

        // Return completed task
        return base.ExecuteResultAsync(context);
    }
}
2 Likes