You are viewing limited content. For full access, please sign in.

Question

Question

How can I send a file as "raw" data as part of HTTP Form Post activity?

asked on September 10, 2016 Show version history

I'm working with another system that expects a file as part of a POST request. When I test the system's API using the web-based test tool they provide, it works fine. This is what the request looks like in Fiddler:

You'll note that the data in the File field looks gibberish. This is because the test tool is actually sending the file as raw data (the web browser handles that part), and Fiddler attempts to encode it as ASCII. But the test itself succeeds.

OK, so here is how I try to simulate this in Workflow...

I'm first reading the edoc using a script and putting its data into a MemoryStream. Then I convert that stream into an array and concatenate the contents into one string, which I store in a token called %(Binary), which has a token tag of "File".

protected override void Execute()
{
    DocumentInfo di = this.BoundEntryInfo as DocumentInfo;
    string contentType = "application/msword";
    string binaryValue;

    using (LaserficheReadStream file = di.ReadEdoc(out contentType))
    {
        using (MemoryStream mstream = new MemoryStream())
        {
            file.CopyTo(mstream);
            concatStreamArray = string.Join("", mstream.ToArray());
            SetTokenValue("binary", concatStreamArray);
        }
    }
}

Then I plug that token into the HTTP Form Post activity:

When I test the workflow though, this is what Fiddler shows:

You'll note that Workflow is sending a string representation of the data, rather than the raw data like the test tool. It even automatically sets Content-Transfer-Encoding to binary, but doesn't convert the string to binary (like it should, since the token has a "File" tag and Field type is set to "File").

I tried to put the MemoryStream object directly into the %(binary) token, but the HTTP activity gets canceled with "Attempted to access an unloaded AppDomain" error when I do that.

I also tried setting the token tag of %(binary) to Byte Array and storing mstream.ToArray() inside %(binary), but when I do that, Workflow sends only the first byte in the array when it makes the POST request.

So the question is: can Workflow make an HTTP Form Post request that looks like the first Fiddler screenshot above?

0 0

Replies

replied on September 11, 2016 Show version history

I ended up writing a script that hand-crafts the request payload and sends it using System.Net.WebClient.

public WebHeaderCollection UploadMultipart(byte[] file, string filename, string contentType, string url)
{
    WebClient webClient = new WebClient();
    string boundary = "------------------------" + DateTime.Now.Ticks.ToString("x");
    webClient.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
    string fileData = webClient.Encoding.GetString(file);
    string package = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"ApiKey\"\r\n\r\nabcdefg\r\n--{0}\r\n" +
                                "Content-Disposition: form-data; name=\"File\"; filename=\"Document1.docx\"\r\nContent-Type: {2}\r\n\r\n{3}\r\n--{0}\r\n", boundary, filename, contentType, fileData);
    byte[] nfile = webClient.Encoding.GetBytes(package);
    byte[] resp = webClient.UploadData(url, "POST", nfile);
    return webClient.ResponseHeaders;
}

I then take the edoc, convert it to a byte array and send it to the API using the above function:

protected override void Execute()
{
    DocumentInfo di = this.BoundEntryInfo as DocumentInfo;
    string uri = GetTokenValue("uri").ToString();
    string contentType = "application/msword";
    byte[] byteArray;

    using (LaserficheReadStream file = di.ReadEdoc(out contentType))
    {
        using (MemoryStream mstream = new MemoryStream())
        {
            file.CopyTo(mstream);
            byteArray = mstream.ToArray();
        }
    }
    try
    {
        WebHeaderCollection responseHeaders = UploadMultipart(byteArray, "Document1.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", uri);
        SetTokenValue("File URL", responseHeaders["FileUrl"]);
    }catch (WebException e)
    {
        SetTokenValue("error", e.Message);
    }
}

This works perfectly.

I hate writing scripts for workflows though because they make projects less maintainable. I think workflow's HTTP Form Post activity should give the option of selecting a file if the header's field type is "File"... kinda like this:

It would be even better if it was able to detect the Content-type for you based on the file you attached (by reading the MIME type from the edoc).

2 0
replied on September 12, 2016

Sounds like you've got a workaround there already, but anytime I've needed to convert a byte array to a string in .NET, I usually use the System.Text.Encoding tools to make sure it happens the way I want.

If you wanted ASCII for instance, you'd use:

System.Text.Encoding.ASCII.GetString(mstream.ToArray());

1 0
replied on September 12, 2016 Show version history

Thanks, I actually tried that after posting (it was a long weekend, heh), and was able to encode the data correctly, but still couldn't get it to work.

The request on the left is my hand-crafted one, the one on the right (which fails) is workflow's.

Interestingly enough, Workflow always seems to insert the Content-Transfer-Encoding header into the form-data object, even if you leave it blank in the configuration. But I don't think that's the issue - it works fine if I add it to my own function.

0 0
replied on September 12, 2016

Hmm. Looks pretty similar. I'm stumped at this point too... Odd.

0 0
replied on September 12, 2016

I don't know how to get WF to do what you want, but the strange numeric encoding comes from:

concatStreamArray = string.Join("", mstream.ToArray());

Here you call ToString() on each byte in your array and concatenate the results into a String.  If the token value needs to be a String there's no good way around it, but I would try just leaving it as a byte array and see what WF does with it.

1 0
replied on September 12, 2016

Yes, I tried setting the token tag of %(binary) to Byte Array and storing mstream.ToArray() inside %(binary), but when I do that, Workflow sends only the first byte in the array as a string when it makes the POST request.

0 0
You are not allowed to follow up in this post.

Sign in to reply to this post.