How to read a WebClient response after posting data? - c#

Behold the code:
using (var client = new WebClient())
{
using (var stream = client.OpenWrite("http://localhost/", "POST"))
{
stream.Write(post, 0, post.Length);
}
}
Now, how do I read the HTTP output?

It looks like you have a byte[] of data to post; in which case I expect you'll find it easier to use:
byte[] response = client.UploadData(address, post);
And if the response is text, something like:
string s = client.Encoding.GetString(response);
(or your choice of Encoding - perhaps Encoding.UTF8)

If you want to keep streams everywhere and avoid allocating huge arrays of bytes, which is good practise (for example, if you plan to post big files), you still can do it with a derived version of WebClient. Here is a sample code that does it.
using (var client = new WebClientWithResponse())
{
using (var stream = client.OpenWrite(myUrl))
{
// open a huge local file and send it
using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
file.CopyTo(stream);
}
}
// get response as an array of bytes. You'll need some encoding to convert to string, etc.
var bytes = client.Response;
}
And here is the customized WebClient:
public class WebClientWithResponse : WebClient
{
// we will store the response here. We could store it elsewhere if needed.
// This presumes the response is not a huge array...
public byte[] Response { get; private set; }
protected override WebResponse GetWebResponse(WebRequest request)
{
var response = base.GetWebResponse(request);
var httpResponse = response as HttpWebResponse;
if (httpResponse != null)
{
using (var stream = httpResponse.GetResponseStream())
{
using (var ms = new MemoryStream())
{
stream.CopyTo(ms);
Response = ms.ToArray();
}
}
}
return response;
}
}

Related

How to upload an Image on Slack via Slack-App in c# using the byteStream of an image [duplicate]

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?
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.
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

Bittrex websocket encoding

i use this topic
Bittrex websockets encoding method?
bittrex websocket api return json string.
{"C":"d-88903F9C-uIVg,2|Cv,81594","G":"f8N0wWnjI7HYfemJI8e0xCbSANWfxnpNCIT4iAHhWDhZjpcgeesgN6r5uUtMrGXJhuLnOyMI52yEsxDhlxcpAJc6+oPWpOx4b7k2EWza50QZM5sNKXQHzanncDgQPzJQc7yWAg==","M":[]}
I want decode param G(OpenBuyOrders). Firstly i think its gzip string, but decoding method not works
I use python method
gzip.decompress(binascii.a2b_base64(b"f8N0wWnjI7HYfemJI8e0xCbSANWfxnpNCIT4iAHhWDhZjpcgeesgN6r5uUtMrGXJhuLnOyMI52yEsxDhlxcpAJc6+oPWpOx4b7k2EWza50QZM5sNKXQHzanncDgQPzJQc7yWAg=="))
its raise error.
I try make it with c#. Same error. In documentation i not find about this format
public static string Decode(string wireData)
{
try
{
// Step 1: Base64 decode the wire data into a gzip blob
byte[] gzipData = Convert.FromBase64String(wireData);
// Step 2: Decompress gzip blob into minified JSON
using (var decompressedStream = new MemoryStream())
using (var compressedStream = new MemoryStream(gzipData))
using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress))
{
deflateStream.CopyTo(decompressedStream);
decompressedStream.Position = 0;
using (var streamReader = new StreamReader(decompressedStream))
{
return streamReader.ReadToEnd();
}
}
}
catch (Exception ex)
{
return "";
}
}

How to read from Response.OutputStream in C#

I'm using ServiceStack to create a Service. In one of the methods I write some data in response's output stream like this: await response.OutputStream.WriteAsync(Consts.DATA, 0, Consts.DATA.Length); where data is a byte[]. Ok, now the problem is when using this service from a client and GET the HttpResponseMessage for the specified method, I don't know how to get the data out. I wan't to unit test that the actual response contains the data I passed when writing to output content.
(I've tried to ReadAsByteArray but it throws an exception. When reading as Stream or String I don't know how to make it a byte[] to compare it in the test with the excpected Consts.DATA byte array.)
I'm pretty new to the field, excuse my ignorance if i miss something. Any help is appreciated.
Thanks!
If you want to use the typed client, you can do (from the documentation):
As raw bytes:
byte[] responseBytes = client.Get<byte[]>("/poco/World");
var dto = responseBytes.FromUtf8Bytes().FromJson<PocoResponse>();
dto.Result //Hello, World
Or as a Stream:
using (Stream responseStream = client.Get<Stream>("/poco/World")) {
var dto = responseStream.ReadFully()
.FromUtf8Bytes()
.FromJson<PocoResponse>();
dto.Result //Hello, World
}
Or even access the populated HttpWebResponse object:
HttpWebResponse webResponse = client.Get<HttpWebResponse>("/poco/World");
webResponse.Headers["X-Response"] //World
using (var stream = webResponse.GetResponseStream())
using (var sr = new StreamReader(stream)) {
var dto = sr.ReadToEnd().FromJson<PocoResponse>();
dto.Result //Hello, World
}
You can also use untyped ServiceStack client to access raw response, it is described in the documentation with samples.
If all you need is load the data from an URL as a Stream and youre using a HttpClient you can just do this to get a stream back:
var result = await client.GetStreamAsync(URL);
I think you can also use GetResponseStream() on your HttpWebResponse.
var result = message.GetResponseStream();
Run your output stream through this static method:
public static byte[] ReadFullStream(Stream st)
{
var lockTaken = false;
try
{
Monitor.Enter(_lock, ref lockTaken);
var size = 0;
var continueRead = true;
var buffer = (byte[])Array.CreateInstance(typeof(byte), 0x10000);
using (MemoryStream ms = new MemoryStream())
{
while (continueRead)
{
size = st.Read(buffer, 0, buffer.Length);
if (size > 0)
{
ms.Write(buffer, 0, size);
}
else
{
continueRead = false;
}
}
return ms.ToArray();
}
}
finally
{
if (lockTaken) { Monitor.Exit(_lock); }
}
}
EDIT: forgot, you'll need this defined at class scope:
private static readonly object _lock = new object();
Read response stream as follow-
using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
{
responseContent = reader.ReadToEnd();
if (response.StatusCode == HttpStatusCode.OK && response.StatusDescription.ToUpper() == "OK" && !string.IsNullOrEmpty(responseContent))
{
objFSLstGetAgent = JsonConvert.DeserializeObject<YourObjectClass>(responseContent);
}
}

WP8 Gzip compression

WP8 does not support Gzip compression, but there is 3rd party libraries that will allow you do so, i have tried many but i am not able to make it work. this is my latest try:
var handler = new HttpClientHandler();
if (handler.SupportsAutomaticDecompression)
{
handler.AutomaticDecompression = DecompressionMethods.GZip |
DecompressionMethods.Deflate;
}
Uri myUri = new Uri("http://www.web.com");
HttpClient client = new HttpClient(handler);
client.BaseAddress = myUri;
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("ubq-compression", "gzip");
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, myUri);
req.Content = new StringContent(finalURL, Encoding.UTF8);
HttpResponseMessage rm = client.SendAsync(req).Result;
string rst = await rm.Content.ReadAsStringAsync();
the API return to me an array of bytes but the the first 300 are not Gziped but everything else is
i need to unzip everything that comes after the 300 bytes.
i am using the http://www.nuget.org/packages/Microsoft.Net.Http
// i am splitting the array
byte[] hJ = res.Take(300).ToArray();
byte[] bJ = res.Skip(300).ToArray();
bj is what need to be decompressed.
i am trying this
MemoryStream stream = new MemoryStream();
stream.Write(bj, 0, bj.Length);
using (var inStream = new MemoryStream(bj))
{
var bigStreamsss = new GZipStream(inStream, CompressionMode.Decompress, true);
using (var bigStreamOut = new MemoryStream())
{
bigStreamsss.CopyTo(bigStreamOut);
output = Encoding.UTF8.GetString(bigStreamOut.ToArray(), 0, bigStreamOut.ToArray().Length);
}
}
but it is always failing on this line "
var bigStreamsss = new GZipStream(inStream, CompressionMode.Decompress, true);
"
Any help would be much appreciated
If you are using the compression header there's nothing you need to do. The server compresses and the client decompresses automatically and you don't have to worry about anything. However it sounds like you're using some proprietary content compression standard where you only compress some of it. If that's the case don't mess with any compression settings on the http client, and instead use a 3rd party uncompress library. Just seek 300 bytes on the response stream, then pass that stream to the library. You should be able to use my inflater from my gzip library, that you can find here: https://github.com/dotMorten/SharpGIS.GZipWebClient/blob/master/src/SharpGIS.GZipWebClient/GZipDeflateStream.cs
It's extremely light-weight (it's just this one file). First call
myResultStream.Seek(300, SeekOrigin.Begin);
If the stream isn't seekable, you will need to read the first 300 bytes first though.
Then use my class to decompress the rest:
Stream gzipStream = new SharpGIS.GZipInflateStream(myResultStream);
You can now read the gzipStream as if it was an uncompressed stream.
However I really don't understand why you don't use standard http compression and let the server compress everything including the first 300 bytes. It's much easier and better for the all the kittens out there.
You can use http://www.componentace.com/zlib_.NET.htm library (available through nuget):
part of request handler:
try {
var request = (HttpWebRequest)callbackResult.AsyncState;
using (var response = request.EndGetResponse(callbackResult))
using (var stream = response.GetResponseStream()) {
bool zip = false;
if (response.Headers.AllKeys.Contains("Content-Encoding") &&
response.Headers[HttpRequestHeader.ContentEncoding].ToLower() == "gzip") zip = true;
using (var reader = zip ?
#if NETFX_CORE
new StreamReader(new GZipStream(stream, CompressionMode.Decompress))
#else
new StreamReader(new zlib.ZOutputStream(stream), false)
#endif
: new StreamReader(stream)) {
var str = reader.ReadToEnd();
result = new ObjectResult(str);
}
}
}
catch (WebException e) {...}

Download Files from FTP to Client PC [duplicate]

I've been downloading files from an FTP server via the WebClient object that the .NET namespace provides and then write the bytes to a actual file via a BinaryWriter. All is good. However, now, the files have dramatically increased in size and I'm worried about memory constraints so I'd like to create a download stream, create an file stream, and line by line read from the download and write to the file.
I'm nervous since I couldn't find a nice example of this. Here's my end result:
var request = new WebClient();
// Omitted code to add credentials, etc..
var downloadStream = new StreamReader(request.OpenRead(ftpFilePathUri.ToString()));
using (var writeStream = File.Open(toLocation, FileMode.CreateNew))
{
using (var writer = new StreamWriter(writeStream))
{
while (!downloadStream.EndOfStream)
{
writer.Write(downloadStream.ReadLine());
}
}
}
Am I going about this incorrect/better way/etc?
Have you tried the following usage of WebClient class?
using (WebClient webClient = new WebClient())
{
webClient.DownloadFile("url", "filePath");
}
Update
using (var client = new WebClient())
using (var stream = client.OpenRead("..."))
using (var file = File.Create("..."))
{
stream.CopyTo(file);
}
If you want to download file explicitly using customized buffer size:
public static void DownloadFile(Uri address, string filePath)
{
using (var client = new WebClient())
using (var stream = client.OpenRead(address))
using (var file = File.Create(filePath))
{
var buffer = new byte[4096];
int bytesReceived;
while ((bytesReceived = stream.Read(buffer, 0, buffer.Length)) != 0)
{
file.Write(buffer, 0, bytesReceived);
}
}
}

Categories

Resources