I need help with uploading files to Slack.
I have a Slack-App that is working with my code(below) so far. But all I can do is post messages. I can not attach images to the messages - because I do not understand how to use the so called "methods" and the syntax Slack is "showing" on their API-page.
This creates my "content" and below its just a Stream for reading a file I could upload:
public class PostMessage
{
public FormUrlEncodedContent Content(string message, string file)
{
var values = new Dictionary<string, string>
{
{"token", "xoxp-myToken"},
{ "username", "X"},
{ "channel", "myChannel"},
{ "as_user", "false"},
{"text", message},
{ "content", file},
{ "attachments","[{ \"fallback\":\"dummy\", \"text\":\"this is a waste of time\"}]"}
};
var content = new FormUrlEncodedContent(values);
return content;
}
}
public class PostFile
{
String path = #"C:\Users\f.held\Desktop\Held-Docs\dagged.jpg";
public string ReadImageFile()
{
FileInfo fileInfo = new FileInfo(path);
long imageFileLength = fileInfo.Length;
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
byte[] imageData = br.ReadBytes((int)imageFileLength);
var str = Encoding.Default.GetString(imageData);
return str;
}
}
}
The client that communicates:
public class SlackClient
{
private readonly Uri _webhookUrl;
private readonly HttpClient _httpClient = new HttpClient {};
public SlackClient(Uri webhookUrl)
{
_webhookUrl = webhookUrl;
}
public async Task<HttpResponseMessage> SendMessageAsync(FormUrlEncodedContent content)
{
var response = await _httpClient.PostAsync(_webhookUrl, content);
return response;
}
}
}
The Main:
public static void Main(string[] args)
{
Task.WaitAll(IntegrateWithSlackAsync());
}
private static async Task IntegrateWithSlackAsync()
{
var webhookUrl = new Uri("https://slack.com/api/files.upload");
var slackClient = new SlackClient(webhookUrl);
PostMessage PM = new PostMessage();
PostFile PF = new PostFile();
while (true)
{
Console.Write("Type a message: ");
var message = Console.ReadLine();
var testFile = PF.ReadImageFile();
FormUrlEncodedContent payload = PM.Content(message, testFile);
var response = await slackClient.SendMessageAsync(payload);
var isValid = response.IsSuccessStatusCode ? "valid" : "invalid";
Console.WriteLine($"Received {isValid} response.");
Console.WriteLine(response);
response.Dispose();
}
}
}
}
If somebody has an example on what a upload has to look like. Or even better,
if somebody could really explain the syntax these Slack-Messages have to have.
That would be great! I still do not know where and HOW I should put the so called
"Accepted content types: multipart/form-data, application/x-www-form-urlencoded" to my upload. I just can not find examples on this...
Edit:
What confuses me needlesly is that Slack states they have an extra method called file.upload - but we shouldn't use it anymore, we should use just postMessage.
But how would I "pack" a file in a message? My syntax always seems to be off. Especially when it comes to "content"...
I just can not figure out what the c#-code has to look like. Where do I declare the aforementioned "content type"?
Another problem is, it always sends my messages through - means I get a 200-response from the server. But it never shows the file (which probably means the syntax is off) Or I get the 200-response but the message never shows in Slack.
Images in message
If you want to include an image in your message (along with some text) you can do so by adding images as message attachment to a normal message send with chat.postMessage.
For that you need a public URL of your image and that link with the image_url property to an attachment. That attachment can also contain text, and you can add multiple attachments to your message.
This is how it looks like:
And here is how this message looks in JSON:
{
"channel": "test",
"text": "This is a message example with images in the attachment",
"attachments": [
{
"fallback": "game over",
"text": "This is some text in the attachement",
"image_url": "https://i.imgur.com/jO9N3eJ.jpg"
}
]
}
Uploading images
The image URL needs to be publicly accessible on the Internet. So you need to host your image file on a public webserver or upload it to a image cloud service (e.g. imgur.com).
You can also use Slack as cloud service for your images. Here is how that works:
Upload to Slack: Upload your image to your Slack workspace with files.upload
Get public URL: Get a public URL for your image file with files.sharedPublicURL. Normally all files on Slack are private, but you can only use public URLs for message attachments.
Send message: Include your image as attachment in a message: Use the permalink_public property of your image file as value for image_url
Example code
Here is a full working example in C# for first uploading an image to Slack and then using it in a message.
Note: This example requires Newtonsoft.Json.
using System;
using System.Net;
using System.Collections.Specialized;
using System.Text;
using Newtonsoft.Json;
public class SlackExample
{
// classes for converting JSON respones from API method into objects
// note that only those properties are defind that are needed for this example
// reponse from file methods
class SlackFileResponse
{
public bool ok { get; set; }
public String error { get; set; }
public SlackFile file { get; set; }
}
// a slack file
class SlackFile
{
public String id { get; set; }
public String name { get; set; }
public String permalink_public { get; set; }
}
// reponse from message methods
class SlackMessageResponse
{
public bool ok { get; set; }
public String error { get; set; }
public String channel { get; set; }
public String ts { get; set; }
}
// a slack message attachment
class SlackAttachment
{
public String fallback { get; set; }
public String text { get; set; }
public String image_url { get; set; }
}
// main method with logic
public static void Main()
{
String token = "xoxp-YOUR-TOKEN";
/////////////////////
// Step 1: Upload file to Slack
var parameters = new NameValueCollection();
// put your token here
parameters["token"] = token;
var client1 = new WebClient();
client1.QueryString = parameters;
byte[] responseBytes1 = client1.UploadFile(
"https://slack.com/api/files.upload",
"C:\\Temp\\Stratios_down.jpg"
);
String responseString1 = Encoding.UTF8.GetString(responseBytes1);
SlackFileResponse fileResponse1 =
JsonConvert.DeserializeObject<SlackFileResponse>(responseString1);
String fileId = fileResponse1.file.id;
/////////////////////
// Step 2: Make file public and get the URL
var parameters2 = new NameValueCollection();
parameters2["token"] = token;
parameters2["file"] = fileId;
var client2 = new WebClient();
byte[] responseBytes2 = client2.UploadValues("https://slack.com/api/files.sharedPublicURL", "POST", parameters2);
String responseString2 = Encoding.UTF8.GetString(responseBytes2);
SlackFileResponse fileResponse2 =
JsonConvert.DeserializeObject<SlackFileResponse>(responseString2);
String imageUrl = fileResponse2.file.permalink_public;
/////////////////////
// Step 3: Send message including freshly uploaded image as attachment
var parameters3 = new NameValueCollection();
parameters3["token"] = token;
parameters3["channel"] = "test_new";
parameters3["text"] = "test message 2";
// create attachment
SlackAttachment attachment = new SlackAttachment();
attachment.fallback = "this did not work";
attachment.text = "this is anattachment";
attachment.image_url = imageUrl;
SlackAttachment[] attachments = { attachment };
parameters3["attachments"] = JsonConvert.SerializeObject(attachments);
var client3 = new WebClient();
byte[] responseBytes3 = client3.UploadValues("https://slack.com/api/chat.postMessage", "POST", parameters3);
String responseString3 = Encoding.UTF8.GetString(responseBytes3);
SlackMessageResponse messageResponse =
JsonConvert.DeserializeObject<SlackMessageResponse>(responseString3);
}
}
Here is a shorter working example showing how to just upload any file to Slack with C# only. The example will also automatically share the file the given channel.
I have included the logic to convert the API response from JSON, which will always be needed to determine if the API call was successful.
Note: This example requires Newtonsoft.Json
using System;
using System.Net;
using System.Collections.Specialized;
using System.Text;
using Newtonsoft.Json;
public class SlackExample
{
// classes for converting JSON respones from API method into objects
// note that only those properties are defind that are needed for this example
// reponse from file methods
class SlackFileResponse
{
public bool ok { get; set; }
public String error { get; set; }
public SlackFile file { get; set; }
}
// a slack file
class SlackFile
{
public String id { get; set; }
public String name { get; set; }
}
// main method with logic
public static void Main()
{
var parameters = new NameValueCollection();
// put your token here
parameters["token"] = "xoxp-YOUR-TOKEN";
parameters["channels"] = "test";
var client = new WebClient();
client.QueryString = parameters;
byte[] responseBytes = client.UploadFile(
"https://slack.com/api/files.upload",
"D:\\temp\\Stratios_down.jpg"
);
String responseString = Encoding.UTF8.GetString(responseBytes);
SlackFileResponse fileResponse =
JsonConvert.DeserializeObject<SlackFileResponse>(responseString);
}
}
About content types: Those are part of the header of a HTTP request and can be set manually in the WebClient object (see also this answer). However, for our case you can ignore it, because the default content types that WebClient is using for the POST request will work just fine.
Also see this answer on how to upload files with the WebClient class.
Here is another complete example for uploading a file to Slack, this time using the async approach with HttpClient.
Note: This example requires Newtonsoft.Json.
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
namespace SlackExample
{
class UploadFileExample
{
private static readonly HttpClient client = new HttpClient();
// classes for converting JSON respones from API method into objects
// note that only those properties are defind that are needed for this example
// reponse from file methods
class SlackFileResponse
{
public bool ok { get; set; }
public String error { get; set; }
public SlackFile file { get; set; }
}
// a slack file
class SlackFile
{
public String id { get; set; }
public String name { get; set; }
}
// sends a slack message asynchronous
// throws exception if message can not be sent
public static async Task UploadFileAsync(string token, string path, string channels)
{
// we need to send a request with multipart/form-data
var multiForm = new MultipartFormDataContent();
// add API method parameters
multiForm.Add(new StringContent(token), "token");
multiForm.Add(new StringContent(channels), "channels");
// add file and directly upload it
FileStream fs = File.OpenRead(path);
multiForm.Add(new StreamContent(fs), "file", Path.GetFileName(path));
// send request to API
var url = "https://slack.com/api/files.upload";
var response = await client.PostAsync(url, multiForm);
// fetch response from API
var responseJson = await response.Content.ReadAsStringAsync();
// convert JSON response to object
SlackFileResponse fileResponse =
JsonConvert.DeserializeObject<SlackFileResponse>(responseJson);
// throw exception if sending failed
if (fileResponse.ok == false)
{
throw new Exception(
"failed to upload message: " + fileResponse.error
);
}
else
{
Console.WriteLine(
"Uploaded new file with id: " + fileResponse.file.id
);
}
}
static void Main(string[] args)
{
// upload this file and wait for completion
UploadFileAsync(
"xoxp-YOUR-TOKEN",
"C:\\temp\\Stratios_down.jpg",
"test"
).Wait();
Console.ReadKey();
}
}
}
Related
API Testing using RestSharp
I have added config.json file and maintaining all the URL in the file.
Example
{
"Url1": "http://localhost:45677",
"Url2": "http://localhost:45897"
}
I would like to fetch particular URL from the config.json based on the given parameter added in class2
public class class2
{
public void Url1_Request()
{
var client = class1.RestClient("Url1");
}
public void Url2_Request()
{
var client = class1.RestClient("Url2");
}
}
public class class1
{
public static RestClient RestClient(string url)
{
var client = new RestClient(url);
return client;
}
}
you can use this code
using Newtonsoft.Json;
.....
public static RestClient restClient()
{
string json = string.Empty;
using (StreamReader r = new StreamReader(#"C:\...\config.json"))
json = r.ReadToEnd();
var jsonObject=JObject.Parse(json);
var url=GetUrl(jsonObject,"Url1");
return new RestClient(url);
}
public string GetUrl(JObject jsonObject, string url)
{
return (string) jsonObject[url];
}
url
http://localhost:45677
You need to parse the .json file to object. If you don't have some build in function for it, you can use some third party library. Some example is described HERE
May be you need to change structure of .json file to Dictionary or array
I am attempting to use the AmazonSimpleEmailService client via the AWS-SDK for .Net, to send a SendBulkTempatedEmailRequest. I have implemented a dedicated handler for actually building the request and making the SendBulkTemplatedEmailAsync call. It is not working as I expect. I think there is a bug with how the request object is serialized and passed to the API.
Here is some sample code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;
using Newtonsoft.Json;
namespace Sample.AWS.SES
{
public class SendEmailService
{
private readonly IAmazonSimpleEmailService _sesClient;
public SendEmailService(IAmazonSimpleEmailService sesClient)
{
_sesClient = sesClient;
}
public async Task<string> SendBulkEmailAsync(SesOrderCreatedBulkTemplate data)
{
var result = string.Empty;
var request = new SendBulkTemplatedEmailRequest
{
Template = data.Template,
ConfigurationSetName = data.ConfigurationSet,
DefaultTemplateData = JsonConvert.SerializeObject(data.DefaultTemplateData),
Source = data.Source,
Destinations = data.Destinations
.Select(d => new BulkEmailDestination
{
Destination = new Destination
{
ToAddresses = d.ToAddresses.ToList(),
},
ReplacementTemplateData = string.Empty
})
.ToList(),
ReplyToAddresses = data.ReplyToAddresses.ToList()
};
try
{
var resp = await _sesClient.SendBulkTemplatedEmailAsync(request);
}
catch (Exception ex)
{
var msgEx = new Exception("Error sending message to SES.", ex);
throw msgEx;
}
return result;
}
public class SesOrderCreatedBulkTemplate
{
public string Source { get; set; }
public string Template { get; set; }
public string ConfigurationSet { get; set; }
public IEnumerable<Destination> Destinations { get; set; }
public MyTemplateData DefaultTemplateData { get; set; }
public IEnumerable<string> ReplyToAddresses { get; set; }
public string ReturnPath { get; set; } = string.Empty;
}
public class DestinationObj
{
public IEnumerable<string> ToAddresses { get; set; }
public MyTemplateData ReplacementTemplateData { get; set; }
public DestinationObj() {}
}
public class MyTemplateData
{
public List<Person> Tenants { get; set; }
}
public class Person
{
public string PersonName { get; set; }
public List<object> PersonData { get; set; }
}
}
}
The properties for SourceArn, TemplateArn and ReturnPathArn are omitted on purpose. According the SES documentation, the SDK wraps the low-level functionality of the Amazon SES API with higher-level data types and function calls that take care of the details for you. When I view the API documentation for sending bulk email, the ARN properties are all list as not required. When I look at the some CLI examples, it is the same. When I look at the documentation for the SDK for .Net v3, it is ambiguous (not marked as required or optional).
Because the SDK supposed to wrap the low-level functionality of the API, I do not believe the ARN values are required (neither the API nor the CLI require them). However, when I attempt to actually use the request object created in the code snippet, I get an error that says InvalidTemplateData.
If I serialize the request object to JSON, then remove the 3 ARN fields from the string, I can use either the API or the CLI to successfully send the message.
In addition to not specifying a value for the ARN's, I have tried (for all 3 ARN values):
specificArn = string.empty;
specificArn = new {};
specificArn = "";
I have also tried explicitly newing-up the object separate from initializing the properties:
var request = new SendBulkTemplatedEmailRequest();, and then individually populating the properties.
If I don't initialize the ARN values, I get an error about NoneType vs StringType when the send method is called. The variations on string initialization that I tried result in InvalidTemplateData errors.
Note, I do know ARN values for Source and ReturnPath. I do not have an ARN value for the template we use. Supposedly, using the CLI, when you create a template you should receive a response back that includes the ARN for the template. I get no response from the CLI when I create a template, but it does get created every time I try. The describe-template CLI command is not valid when you specify SES and responds with an error if I don't specify the workspace (whatever you call the SES space) value.
Does anyone have a suggestion on how to solve this?
From the provided code it's hard to say what you pass into API.
This is how I send bulk emails:
SES configuration
create a template (taken from AWS SES docs) and save it to a file - my-template.json
{
"Template": {
"TemplateName": "my-template",
"SubjectPart": "Greetings, {{name}}!",
"HtmlPart": "<h1>Hello {{name}},</h1><p>Your favorite animal is {{favoriteanimal}}.</p>",
"TextPart": "Dear {{name}},\r\nYour favorite animal is {{favoriteanimal}}."
}
}
create a template via CLI aws ses create-template --cli-input-json file://my-template.json
SES .NET SDK API
async Task SendAsync(string[] receivers)
{
var destinations = receivers
.Select(receiver => new BulkEmailDestination
{
Destination = new Destination(new List<string> { receiver }),
ReplacementTemplateData = ToJson(receiver, "Doggy")
})
.ToList();
var bulkTemplate = new SendBulkTemplatedEmailRequest
{
Source = "your-email#gmail.com", // your email you bulk send from
Template = "my-template", // your template name
DefaultTemplateData = ToJson("<not set>", "<not set>"),
Destinations = destinations
};
await _client.SendBulkTemplatedEmailAsync(bulkTemplate);
}
// Create replacement data by serializing Dictionary
string ToJson(string name, string favoriteanimal)
=> JsonSerializer.Serialize(new Dictionary<string, string>
{
{ "name", name },
{ "favoriteanimal", favoriteanimal }
});
New to C# here and to running asynchronous tasks.
I'm trying to scrape some music album info from a website. The webpage's search produces a JSON object in plaintext, but I can't seem to access any DOM info. Here's what I tried (using HtmlAgilityPack):
using HtmlAgilityPack;
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
public async Task<String> AlbumScraper(string albumname) {
HtmlWeb web = new HtmlWeb();
string albumurl = Uri.EscapeUriString("https://www.metal-archives.com/search/ajax-album-search/?field=title&query=" + albumname);
Console.Write(albumurl);
var albumdoc = await Task.Factory.StartNew(() => web.Load(albumurl));
string albumjson = "";
if (albumdoc.DocumentNode != null) {
albumjson = albumdoc.DocumentNode.InnerText;
}
return albumjson;
}
private async void Form1_Load(object sender, EventArgs e) {
string rawtext = await AlbumScraper("rust+in+peace");
Console.Write(rawtext);
}
}
}
How can I grab the JSON text generated? I can see it clearly when I load the "albumurl" URL...
You can generate it by online tool http://json2csharp.com/
Then add generated class to your code
public class AlbumSearchResponse
{
public string error { get; set; }
public int iTotalRecords { get; set; }
public int iTotalDisplayRecords { get; set; }
public int sEcho { get; set; }
public List<List<string>> aaData { get; set; }
}
convert your response text to class
var data = JsonConvert.DeserializeObject<AlbumSearchResponse>(response);
foreach (var item in data.aaData)
{
//do whatever your want with data
}
also you need add newtonsoft json package from nuget to get JsonConvert working
Firstly you don't need HtmlAgilityPack.
Secondly,try:
using Newtonsoft.Json.Linq;
string albumurl = Uri.EscapeUriString("https://www.metal-archives.com/search/ajax-album-search/?field=title&query=rust+in+peace");
string doc = "";
using (System.Net.WebClient client = new System.Net.WebClient()) // WebClient class inherits IDisposable
{
doc = client.DownloadString(albumurl);
}
then you could deserialize it (#itikhomi)
AlbumSearchResponse data = JsonConvert.DeserializeObject<AlbumSearchResponse>(doc);
You could also parse it manually
JObject json = JObject.Parse(doc);
string error= Convert.ToString(json["error"]);
. . .
string aaData= Convert.ToString(json["aaData"]);
JArray arr = JArray.Parse(aaData);
foreach(JToken token in arr)
{
string[] strarr = token.ToObject<string[]>();
}
Metal archive's album list is provided via API, but the album's details is placed directly in DOM. There is a .NET standard wrapper library for metal archives: MetalArchivesNET.
For now it only allows you to find band/album/song by name, but in the future it will be able to get content by url
Iam using CefSharp's SchemeHandler in order to grab resources from my C# project like .css, .js or .png files using a custom url for example custom://cefsharp/assets/css/style.css
I've 2 custom classes in order to archive this.
First class, MyCustomSchemeHandlerFactory will be the one that handles the custom Scheme and it looks like this, where "custom" will be the custom scheme:
internal class MyCustomSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "custom";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new MyCustomSchemeHandler();
}
}
The next class I've implemented is MyCustomSchemeHandler which will receive the call and output a response and it looks like this:
internal class MyCustomSchemeHandler : IResourceHandler
{
private static readonly IDictionary<string, string> ResourceDictionary;
private string mimeType;
private MemoryStream stream;
static MyCustomSchemeHandler()
{
ResourceDictionary = new Dictionary<string, string>
{
{ "/home.html", Properties.Resources.index},
{ "/assets/css/style.css", Properties.Resources.style}
};
}
public Stream Stream { get; set; }
public int StatusCode { get; set; }
public string StatusText { get; set; }
public string MimeType { get; set; }
public NameValueCollection Headers { get; private set; }
public Stream GetResponse(IResponse response, out long responseLength, out string redirectUrl)
{
redirectUrl = null;
responseLength = -1;
response.MimeType = MimeType;
response.StatusCode = StatusCode;
response.StatusText = StatusText;
response.ResponseHeaders = Headers;
var memoryStream = Stream as MemoryStream;
if (memoryStream != null)
{
responseLength = memoryStream.Length;
}
return Stream;
}
public bool ProcessRequestAsync(IRequest request, ICallback callback)
{
// The 'host' portion is entirely ignored by this scheme handler.
var uri = new Uri(request.Url);
var fileName = uri.AbsolutePath;
string resource;
if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
{
var resourceHandler = ResourceHandler.FromString(resource);
stream = (MemoryStream)resourceHandler.Stream;
var fileExtension = Path.GetExtension(fileName);
mimeType = ResourceHandler.GetMimeType(fileExtension);
callback.Continue();
return true;
}
else
{
callback.Dispose();
}
return false;
}
void GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl)
{
responseLength = stream == null ? 0 : stream.Length;
redirectUrl = null;
response.StatusCode = (int)HttpStatusCode.OK;
response.StatusText = "OK";
response.MimeType = mimeType;
}
bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback)
{
//Dispose the callback as it's an unmanaged resource, we don't need it in this case
callback.Dispose();
if (stream == null)
{
bytesRead = 0;
return false;
}
//Data out represents an underlying buffer (typically 32kb in size).
var buffer = new byte[dataOut.Length];
bytesRead = stream.Read(buffer, 0, buffer.Length);
dataOut.Write(buffer, 0, buffer.Length);
return bytesRead > 0;
}
bool CanGetCookie(Cookie cookie)
{
return true;
}
bool CanSetCookie(Cookie cookie)
{
return true;
}
void Cancel()
{
}
}
Inside this class I've defined a custom resource dictionary which will dictate what file from the resources will be used, so as I stated in the first example, custom://cefsharp/assets/css/style.css should load the resource Properties.Resources.style, the problem is that nothing gets loaded once I enter to the specific url, I've tried to output the mimeType and It works but somehow the file itself won't output correctly. Is there something wrong with my implementation?
Additionaly I've tried to output the raw file in the form of:
if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
{
MessageBox.Show(resource);
}
And it outputs the correct file without any problems.
To load the custom Scheme I use the following code before initializing CefSharp:
var settings = new CefSettings();
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = MyCustomSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new MyCustomSchemeHandlerFactory()
});
The above classes were based on the following links:
MyCustomSchemeHandlerFactory: FlashResourceHandlerFactory.cs
MyCustomSchemeHandler: CefSharpSchemeHandler.cs and ResourceHandler.cs
Since Cefsharp changed a bit in last few months here is an updated and easier way of handling 'file' protocol. I wrote blog post on this matter.
What you want to add is your scheme handler and its factory:
using System;
using System.IO;
using CefSharp;
namespace MyProject.CustomProtocol
{
public class CustomProtocolSchemeHandler : ResourceHandler
{
// Specifies where you bundled app resides.
// Basically path to your index.html
private string frontendFolderPath;
public CustomProtocolSchemeHandler()
{
frontendFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "./bundle/");
}
// Process request and craft response.
public override bool ProcessRequestAsync(IRequest request, ICallback callback)
{
var uri = new Uri(request.Url);
var fileName = uri.AbsolutePath;
var requestedFilePath = frontendFolderPath + fileName;
if (File.Exists(requestedFilePath))
{
byte[] bytes = File.ReadAllBytes(requestedFilePath);
Stream = new MemoryStream(bytes);
var fileExtension = Path.GetExtension(fileName);
MimeType = GetMimeType(fileExtension);
callback.Continue();
return true;
}
callback.Dispose();
return false;
}
}
public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "customFileProtocol";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new CustomProtocolSchemeHandler();
}
}
}
And then register it before calling Cef.Initialize:
var settings = new CefSettings
{
BrowserSubprocessPath = GetCefExecutablePath()
};
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});
If you simply need to return a string, then you can use ResourceHandler.FromString(html, mimeType). For this you just need to implement the ISchemeHandlerFactory.
https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp/ResourceHandler.cs#L98
Example reading from a file https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp.Example/CefSharpSchemeHandlerFactory.cs#L17 which can be translated to reading from a string quite simply.
public sealed class ImgurUpload
{
public event EventHandler<UploadCompleteEventArgs> UploadComplete;
public void PostToImgur(string location, string key, string name = "", string caption = "")
{
try
{
using (var webClient = new WebClient())
{
NameValueCollection values = new NameValueCollection
{
{ "image", ConvertToBase64(location) },
{ "key", key },
{ "name", name },
{ "caption", caption}
};
webClient.UploadValuesAsync(new Uri("http://api.imgur.com/2/upload.xml"), "POST", values);
webClient.UploadValuesCompleted += ((sender, eventArgs) =>
{
byte[] response = eventArgs.Result;
XDocument result = XDocument.Load(XmlReader.Create(new MemoryStream(response)));
if (UploadComplete != null)
UploadComplete(this, new UploadCompleteEventArgs(result));
});
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private string ConvertToBase64(string imageLocation)
{
byte[] imageData = null;
using (FileStream fileStream = File.OpenRead(imageLocation))
{
imageData = new byte[fileStream.Length];
fileStream.Read(imageData, 0, imageData.Length);
}
return Convert.ToBase64String(imageData);
}
}
public class UploadCompleteEventArgs : EventArgs
{
public string Original { get; private set; }
public string ImgurPage { get; private set; }
public string DeletePage { get; private set; }
public UploadCompleteEventArgs(XDocument xmlDoc)
{
var objLinks = from links in xmlDoc.Descendants("links")
select new
{
original = links.Element("original").Value,
imgurPage = links.Element("imgur_page").Value,
deletePage = links.Element("delete_page").Value
};
Original = objLinks.FirstOrDefault().original;
ImgurPage = objLinks.FirstOrDefault().imgurPage;
DeletePage = objLinks.FirstOrDefault().deletePage;
}
}
Above is a class I wrote to upload an image to imgur using the Anonymous API. I have used the API in the past and have always found it to be considerably slower than the website uploader and slower than other .NET applications that use web requests to effectively send data to the website directly rather than using the API.
I posted the full class above as it may be something I'm doing (or not doing) that's causing the issue. I'd really appreciate it if anybody can identify the issue for me.
I did some fair testing earlier today and one result for example, is as followed:
800kb image via the imgur website = 35 seconds
800kb image via using my class = 1minute 20 seconds
The one you are uploading is ~35% bigger because you're uploading it as a STRING.
Upload via bytes and it should be just as fast.