如何从 http 触发器 azure 函数运行 .cmd 文件



我想使用 azure 函数运行 .cmd 文件。我想在后台进程而不是 azure 函数的主进程中运行它。

我已经使用 Kudu 编辑器将 cmd 文件保存在 azure 函数平台上。我可以在本地运行它,但在部署后它根本不起作用(我也没有收到任何错误(。

      string cmdFileLocation = @"D:homejobs.cmd";
       Process proc = new Process();
       ProcessStartInfo info = new ProcessStartInfo();
       try
       {
           info.FileName = cmdFileLocation;
           info.Arguments = name;
           info.WindowStyle = ProcessWindowStyle.Minimized;
           info.UseShellExecute = false;
           proc.StartInfo = info;
           info.RedirectStandardOutput = true;
           info.RedirectStandardError = true;
           proc.Start();
           proc.WaitForExit();
        }
        catch (Exception ex)
        {
            log.LogInformation("Exception Occurred :{0},{1}", ex.Message, ex.StackTrace.ToString());
        }

为了进行测试,cmd文件中有curl命令。curl 将使用 azure 函数在本地计算机上触发,因为我可以看到请求即将到来 (https://webhook.site(,但在弃用后什么也没发生。

这是让任何.exe/.cmd作为Web服务运行的简单方法。您只需在配置文件中指定 exe/cmd 的输入参数即可。您可以通过指定要从中下载的 URL 来使用二进制文件作为 exe 的输入。

下面是配置文件的外观

{
    "name": "consoleAppToFunctions",
    "input": {
        "command": "ffmpeg.exe -i {inputFile} {output1}",
        "arguments": {
            "inputFile": {
                "url": "https://1drv.ms/v/<link-to-file>"
                //binary file input
            },
            "output1": {
                "localfile": "out.mp3"
                //stored in a temp folder
            }
        }
    },
    "output": {
        "folder": "outputFolder",
        "binaryFile": {
            "returnFile": "*.mp3",
            "returnFileName": "yourFile.mp3"
        }
    }
}

下面是相同的 AZURE 函数代码:

#r "Newtonsoft.Json" 
using System.Net;
using Newtonsoft.Json;
using System.IO;
using System.Diagnostics;
//Code from https://github.com/Azure-Samples/functions-dotnet-migrating-console-apps/edit/master/code/run.csx
//Written by Ambrose http://github.com/efficientHacks and Murali http://github.com/muralivp
public class ExeArg
{
    public string Name { get; set; }
    public string Type { get; set; }
    public string Value { get; set; }
}
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info("C# HTTP trigger function processed a request.");
    string localPath = req.RequestUri.LocalPath;
    string functionName = localPath.Substring(localPath.LastIndexOf('/')+1);
    var json = File.ReadAllText(string.Format(@"D:homesitewwwroot{0}FunctionConfig.json",functionName));
    var config = JsonConvert.DeserializeObject<dynamic>(json);
    var functionArguments = config.input.arguments;
    var localOutputFolder = Path.Combine(@"d:homedata", config.output.folder.Value, Path.GetFileNameWithoutExtension(Path.GetTempFileName()));
    Directory.CreateDirectory(localOutputFolder);
    var workingDirectory = Path.Combine(@"d:homesitewwwroot", functionName + "\bin");
    Directory.SetCurrentDirectory(workingDirectory);//fun fact - the default working directory is d:windowssystem32
    var command = config.input.command.Value;
    var argList = new List<ExeArg>();
    //Parse the config file's arguments
    //handle file system, local file etc. and construct the input params for the actual calling of the .exe
    foreach (var arg in functionArguments)
    {
        var exeArg = new ExeArg();
        exeArg.Name = arg.Name;
        var value = (Newtonsoft.Json.Linq.JObject)arg.Value;
        var property = (Newtonsoft.Json.Linq.JProperty)value.First;
        exeArg.Type = property.Name;
        exeArg.Value = property.Value.ToString();
        var valueFromQueryString = await getValueFromQuery(req, exeArg.Name);
        log.Info("valueFromQueryString name=" + exeArg.Name);
        log.Info("valueFromQueryString val=" + valueFromQueryString);
        if(!string.IsNullOrEmpty(valueFromQueryString))
        {
            exeArg.Value = valueFromQueryString;
            log.Info(exeArg.Name + " " + valueFromQueryString);
        }
        if(exeArg.Type.ToLower() == "localfile" || exeArg.Type.ToLower() == "localfolder")
        {
            exeArg.Value = Path.Combine(localOutputFolder, exeArg.Value);
            exeArg.Type = "string";
        }
        if(string.IsNullOrEmpty(exeArg.Value))
        {
            //throw exception here
        }
        argList.Add(exeArg);
    }
    //call the exe
    Dictionary<string, string> paramList = ProcessParameters(argList, localOutputFolder);
    foreach (string parameter in paramList.Keys)
    {
        command = command.Replace(parameter, paramList[parameter]);
    }
    string commandName = command.Split(' ')[0];
    string arguments = command.Split(new char[] { ' ' }, 2)[1];
    log.Info("the command is " + command);
    log.Info("the working dir is " + workingDirectory);
    Process p = new Process();
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.FileName = commandName; 
    p.StartInfo.Arguments = arguments;
    p.Start();
    string output = p.StandardOutput.ReadToEnd();
    p.WaitForExit();
    File.WriteAllText(localOutputFolder+"\out.txt",output);
    //handle return file
    log.Info("handling return file localOutputFolder=" + localOutputFolder);
    string outputFile = config.output.binaryFile.returnFile.Value;
    string outputFileName = config.output.binaryFile.returnFileName.Value;
    var path = Directory.GetFiles(localOutputFolder, outputFile)[0];
    log.Info("returning this file " + path);
    var result = new FileHttpResponseMessage(localOutputFolder);
    var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
    result.Content = new StreamContent(stream);
    result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
    result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
    {
        FileName = outputFileName
    };
    return result;
}
private static Dictionary<string, string> ProcessParameters(List<ExeArg> arguments, string outputFolder)
{
    Dictionary<string, string> paramList = new Dictionary<string, string>();
    foreach (var arg in arguments)
    {
        switch (arg.Type)
        {
            case "url":
                string downloadedFileName = ProcessUrlType((string)arg.Value, outputFolder);
                paramList.Add("{" + arg.Name + "}", downloadedFileName);
                break;
            case "string":
                paramList.Add("{" + arg.Name + "}", arg.Value.ToString());
                break;
            default:
                break;
        }
    }
    return paramList;
}
//you can modify this method to handle different URLs if necessary
private static string ProcessUrlType(string url, string outputFolder)
{
    Directory.CreateDirectory(outputFolder);
    string downloadedFile = Path.Combine(outputFolder, Path.GetFileName(Path.GetTempFileName()));
    //for oneDrive links 
    HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(url);
    webRequest.AllowAutoRedirect = false;
    WebResponse webResp = webRequest.GetResponse();
    webRequest = (HttpWebRequest)HttpWebRequest.Create(webResp.Headers["Location"].Replace("redir", "download"));
    webResp = webRequest.GetResponse();
    string fileUrl = webResp.Headers["Content-Location"];
    WebClient webClient = new WebClient();
    webClient.DownloadFile(fileUrl, downloadedFile);
    return downloadedFile;
}
private async static Task<string> getValueFromQuery(HttpRequestMessage req, string name)
{
    // parse query parameter
    string value = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, name, true) == 0)
        .Value;
    //if not found in query string, look for it in the body (json)
    // Get request body
    dynamic data = await req.Content.ReadAsAsync<object>();
    // Set name to query string or body data
    value = value ?? data?[name];
    return value;
}
//this is to delete the folder after the response
//thanks to: https://stackoverflow.com/a/30522890/2205372
public class FileHttpResponseMessage : HttpResponseMessage
{
    private string filePath;
    public FileHttpResponseMessage(string filePath)
    {
        this.filePath = filePath;
    }
    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        Content.Dispose();
        Directory.Delete(filePath,true);
    }
}

在这里,您可以找到有关此内容的更多信息。希望对您有所帮助。

最新更新