Fabian Held
Fabian Held

Reputation: 403

"no_file_data" error when trying to upload file to slack

when I try to upload any kidn of file through my SlackApp(via c# using HttpClient), I allways get the following response:

{"ok":false,"error":"no_file_data"}

I checked my ByteArray (I stream the file to an array and then try to upload) and wrote my data back into a .txt and .jpg - I tried both types of data. When i write them back they are exact copies from the original, so I guess my streaming and writing to an ByteArrayworks fine. But something is off with my upload.

I'll show you my code: The Client and the method to upload:

using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Net.Http.Headers;


namespace SlackApp
{
    public class SlackClient
    {
        private readonly Uri _webhookUrl;
        private readonly HttpClient _httpClient = new HttpClient {};

        public SlackClient(Uri webhookUrl)
        {
            _webhookUrl = webhookUrl;
        }

        public async Task<HttpResponseMessage> UploadFile(byte[] file)
        {
            var requestContent = new MultipartFormDataContent();
            var fileContent = new ByteArrayContent(file);
            fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
            requestContent.Add(fileContent, "slack", "slack.txt");

            var response = await _httpClient.PostAsync(_webhookUrl, requestContent);
            return response;
        }
    }
}

the creation of the bytearray:

public class PostFile
{
    String path = @"C:\Users\f.held\Desktop\Held-Docs\dagged.jpg";

    public byte[] ReadImageFile()
    {            
        FileInfo fileInfo = new FileInfo(path);
        long imageFileLength = fileInfo.Length;
        FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
        BinaryReader br = new BinaryReader(fs);
        byte[] imageData = br.ReadBytes((int)imageFileLength);
        return imageData;
    }
}

the Main:

using System;
using System.Net.Http;
using System.Threading.Tasks;


namespace SlackApp
{
    class TestArea
    {
        public static void Main(string[] args)
        {    
            Task.WaitAll(IntegrateWithSlackAsync());
        }

        private static async Task IntegrateWithSlackAsync()
        {
            var webhookUrl = new Uri("https://slack.com/api/files.upload?token=xoxp-hereStandsMyToken&channel=MyChannel");  
            var slackClient = new SlackClient(webhookUrl);
            PostMessage PM = new PostMessage();
            PostFile PF = new PostFile();
            var testFile = PF.ReadImageFile();

            while (true)
            {
                var message = Console.ReadLine(); 
                FormUrlEncodedContent payload = PM.Content(message, "");
                var response = await slackClient.SendMessageAsync(payload);

                string content = await response.Content.ReadAsStringAsync();
                Console.WriteLine(content); //I build these two lines in here so I got the response from the method, and this is where it says "no_file_data"

                var isValid = response.IsSuccessStatusCode ? "valid" : "invalid";
                Console.WriteLine($"Received {isValid} response.");
                Console.WriteLine(response); //this puts out a "valid" response - oddly enough
            }
        }
    }
    }

Does anybody have an idea what is wrong here? Why isn't it taking the data?

Upvotes: 5

Views: 8899

Answers (2)

RasTheDestroyer
RasTheDestroyer

Reputation: 1764

I was running into the no_file_data error as well. I found out you the file needs to exist AND it needs actual content inside. Make sure to do a size check or content length check in addition to the file exists check before uploading

Upvotes: 0

Erik Kalkoken
Erik Kalkoken

Reputation: 32854

You have two bugs in your code:

  • main(): The parameter to specify the channels is called channels, not channel
  • UploadFile(): When you add your file content to the multipart you need to include the correct API parameter for the file which is file, not slack. And also want to include a reasonable filename (instead of slack.txt).

Additional comments

  • UploadFile(): Its wrong to set the content type to multipart/form-data. The correct type for that content would be image/jpeg. However, the correct type seams to be detected automatically, so just remove the line.
  • main(): The Slack API will always return OK (http 200, unless there is a network problem), so you want to also look on the ok and error properties of the JSON response instead.

Here is an update version of your code. I changed your main() method to include a call to `UploadFile()?.

using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;


namespace SlackApp
{
    public class PostFile
    {
        string path = @"C:\Users\Stratios_down.jpg";

        public byte[] ReadImageFile()
        {
            FileInfo fileInfo = new FileInfo(path);
            long imageFileLength = fileInfo.Length;
            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
            BinaryReader br = new BinaryReader(fs);
            byte[] imageData = br.ReadBytes((int)imageFileLength);
            return imageData;
        }
    }

    public class SlackClient
    {
        private readonly Uri _webhookUrl;
        private readonly HttpClient _httpClient = new HttpClient { };

        public SlackClient(Uri webhookUrl)
        {
            _webhookUrl = webhookUrl;
        }

        public async Task<HttpResponseMessage> UploadFile(byte[] file)
        {
            var requestContent = new MultipartFormDataContent();
            var fileContent = new ByteArrayContent(file);            
            requestContent.Add(fileContent, "file", "stratios.jpg");

            var response = await _httpClient.PostAsync(_webhookUrl, requestContent);
            return response;
        }
    }

    class TestArea
    {
        public static void Main(string[] args)
        {
            Task.WaitAll(IntegrateWithSlackAsync());
        }

        private static async Task IntegrateWithSlackAsync()
        {
            var webhookUrl = new Uri(
                "https://slack.com/api/files.upload?token=xoxp-MY-TOKEN&channels=test"
            );
            var slackClient = new SlackClient(webhookUrl);

            PostFile PF = new PostFile();
            var testFile = PF.ReadImageFile();

            var response = await slackClient.UploadFile(testFile);

            string content = await response.Content.ReadAsStringAsync();
            Console.WriteLine(content);
            Console.ReadKey();

        }
    }
}

In addition I would have a couple of suggestions to improve your code.

  • Instead of including the additional API parameters in the URL, I would send them in the POST request as recommended by the API documentation.
  • Including the file as FileStream instead of loading it yourself into a ByteArray is the better approach and recommended for larger files.
  • Not sure why you need an infinite loop in your main. Those are really bad and should be avoided.

Please also take also a look at my new async example for uploading a file to Slack where I applied those two ideas.

Upvotes: 2

Related Questions