0

I looked at the messenger documentation at https://developers.facebook.com/docs/messenger-platform/send-messages/#file to try and figure out how to send local attachments. However, when I try it out with a httpclient I get an error saying that the message body can not be empty must provide a valid attachment or message. Below is my code

string fileType = ImageExtensions.Contains(Path.GetExtension(url).ToUpper()) ? "image" : "file";

var multipartContent = new MultipartFormDataContent();

var content = new StringContent($"{{\"attachment\":{{\"type\":\"{fileType}\", \"payload\":{{\"is_reusable\"=true}}}}");

multipartContent.Add(new StringContent($"{{\"id\":\"{long.Parse(recipient)}\"}}"), "recipient");
multipartContent.Add(new StringContent($"{{\"attachment\":{{\"type\":\"{fileType}\", \"payload\":{{\"is_reusable\"=true}}}}"), "message");

var file1 = File.ReadAllBytes(url);
var file2 = new ByteArrayContent(file1);

file2.Headers.Add("Content-Type", GetMimeType(Path.GetExtension(url)));

multipartContent.Add(file2,"filedata", Path.GetFileName(url));
request.Content = multipartContent;

The file type is image and the mime type is image/jpeg. I know the url exists as I checked File.exists

Michael
  • 122
  • 1
  • 2
  • 9
Eric
  • 1
  • 1

2 Answers2

0

Welcome to Stack Overflow!

From the code sample provided it's quite hard to work out what's going wrong as it's not runnable in isolation.

That said, I'd first verify that the image you're consuming by url exists in the format you expect. If you download the .jpeg from the url can you open it on your machine? Assuming it's well structured I'd then try and work out if it's the HttpClient that's malformed - or if there's something wrong with the values you're providing to the Facebook API.

You can do this by creating a simple C# Web API project that listens for a multipart upload on a specific route. See this answer for some sample code on how to do this.

Assuming that you're able to send the .jpeg in question between a local client and a local endpoint accepting a multipart/form-data header then the issue must be with how you're using the Facebook API itself.

In your sample I don't see you using the value of the content variable anywhere. Is this intentional?

If that missing variable is a red-herring then you could try running something along the lines of (making sure to swap the values out as necessary for the ones you're having problems with):

using (var httpClient = new HttpClient())
using (var formDataContent = new MultipartFormDataContent()) 
{
    // Read the file in from a local path first to verify that the image
    // exists in the format you're expecting.
    var fileStream = File.OpenRead("/some/path/image.jpeg");
    using (var streamContent = new StreamContent(fileStream)) 
    {
        // Don't actually call `.Result` you should await this, but for ease of
        // demonstration.
        var imageContent = new ByteArrayContent(streamContent.ReadAsByteArrayAsync().Result);
        imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");

        formDataContent.Add(imageContent, "image", "your-file-name-here");
        formDataContent.Add(new StringContent ($"{{\"id\":\"{long.Parse("your-recipient-here")}\"}}"), "recipient");
        formDataContent.Add(new StringContent($"{{\"attachment\":{{\"type\":\"{"image"}\", \"payload\":{{\"is_reusable\"=true}}}}"), "message");

        // Again don't call `.Result` - await it.
        var response = httpClient.PostAsync("https://some-url-here.com", formDataContent).Result;
    }    
}

If you run the above do you still get an empty message body error?

Michael
  • 122
  • 1
  • 2
  • 9
  • Ah thanks for the response. I didn't mean to never use the content variable. When I tried to create the c# web api project, I got an error saying "Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host." I think that is more referring to the fact I didn't create the api properly though. – Eric Oct 15 '19 at 21:29
  • I also tried to turn the code you posted but I still get the exact same error message. – Eric Oct 15 '19 at 21:35
  • No worries. I've created a [sample application](https://github.com/MichaelSearson/MockMessenger) that works locally for me for testing a POST of a `*.jpeg` file. If you clone that, and update the `MockMessenger.Client` project's `Program.cs` file to have your image request in the `GetImageBytes` method do you see it created as expected on disk & posted without exception? I'll have a closer read through the FB docs now - I may just be missing something obvious in your use of the API. – Michael Oct 16 '19 at 21:04
  • I get an error saying Error reading mime multiparty body part with an inner exception of Maximum request length exceeded. Is my file too big to be uploaded? – Eric Oct 16 '19 at 22:21
  • Ah! That's good to know. What is the size of the file on disk? By default IIS limits to a few MB per request. You can bump this up in the web.config file though - take a look at [this answer](https://stackoverflow.com/questions/3853767/maximum-request-length-exceeded). FYI the Facebook Send API has a hard limit of 25MB per file upload - as per [this doc page](https://developers.facebook.com/docs/messenger-platform/reference/send-api/). – Michael Oct 16 '19 at 23:11
  • Yes I knew about the 25mb limit. The file is 5.79 mb – Eric Oct 16 '19 at 23:25
  • I increased my limit and everything seems to work which implies that it is the values I supply to facebook which must be wrong. – Eric Oct 17 '19 at 14:09
  • Gotcha, that's eliminated all other possibilities then. I'm not familiar enought with FB's API to see what's wrong with your sample code. I'll need to have a play around with it myself. In the meantime if you run the sample cURL command they have in their docs (substituting the values as necessary) does it work? – Michael Oct 17 '19 at 20:00
  • Is there a way to issue a curl request not in the command line? I tried to just copy paste into the command line but the ordering of the commands from facebook seems to be in the wrong order as I get errors in the command line about no url and such – Eric Oct 17 '19 at 20:24
  • Sort of. I've come across GUI applications that translate curl snippets into "more readable" HTTP requests. For example [Insomnia](https://insomnia.rest/) has worked for me in the past. Pasting FB's curl snippet into the URL textbox generated a multipart POST query that I could then run. (Similar to Postman if you've ever used it - but I don't think Postman offers curl translations). – Michael Oct 18 '19 at 19:55
  • RE: the CLI though, if you're using Powershell and hitting issues it's probably because "curl" is aliased to: "Invoke-WebRequest" by default (which is a pain). If you run: `Remove-item alias:curl` you should be able to now issue "real" curl commands without it complaining at you for invalid arguments. If you paste in FB's curl command (and replace the backslash with backtick for indicating new lines) it should work. I get the following back using their raw sample: `{"error":{"message":"Invalid OAuth access token.","type":"OAuthException","code":190,"fbtrace_id":"ADw6GmN3JTEGzaN5AHmcout"}}` – Michael Oct 18 '19 at 19:56
  • Sorry for the late response. I get an error saying recipient parameter is required even though I added it. Here is a link to a screenshot of my curl request: https://drive.google.com/file/d/17ORLd0273oCY1OCFjdiWuKhCmDsIfMhA/view?usp=sharing – Eric Oct 21 '19 at 14:34
0

If you stumble upon this and you're using the FacebookClient from Nuget. This is how I finally managed to upload files to the messages api using vb.net.

Dim Client = New FacebookClient(GetPageAccessToken(PageID))
Dim TmpParams As New Dictionary(Of String, Object)

TmpParams.Add("recipient", "{""id"":""RECIPIENT_ID""}")
TmpParams.Add("message", "{""attachment"":{""type"":""image"", ""payload"":{""url"":"""", ""is_reusable"":false}}}")
TmpParams.Add("messaging_type", "RESPONSE")

Dim Med As New FacebookMediaObject()
Med.FileName = YourFile.FileName
Med.ContentType = YourFile.ContentType
Med.SetValue(YourFile.InputStream.ToBytes())

TmpParams.Add(YourFile.FileName, Med)

Client.Post("me/messages", TmpParams)

Using a Dictionary(of string,object), manually add the params for recipient,message,messaging_type and the file bytes.

J. Minjire
  • 1,003
  • 11
  • 22