Getting Images from S3 - c#

I need to retrieve images from S3. I can't make the folder public and I cant use presigned URL's so all I am left with is GetObject();. Now the image that I'll get back has to be set as a source for an Iframe. To do that I am using a HttpWebHandler. The issue is that if i retrieve a html page it is working fine. But when I try to get an image back, all i get is junk data. Here is my code:
public void ProcessRequest(HttpContext context)
{
NameValueCollection appConfig = ConfigurationManager.AppSettings;
_accessKeyId = appConfig["AWSAccessKey"];
_secretAccessKeyId = appConfig["AWSSecretKey"];
S3 = new AmazonS3Client(_accessKeyId, _secretAccessKeyId);
string responseBody = "";
var request = new GetObjectRequest()
.WithBucketName(bucketName).WithKey("020/images/intro.jpg");
var responseHeaders = new ResponseHeaderOverrides
{
ContentType = "image/jpeg"
};
request.ResponseHeaderOverrides = responseHeaders;
using (var response = S3.GetObject(request))
{
using (var responseStream = response.ResponseStream)
{
using (var reader =
new StreamReader(responseStream))
{
responseBody = reader.ReadToEnd();
}
}
}
context.Response.Write(responseBody);
context.Response.Flush();
context.Response.End();
}
}

There are a couple of problems here:
You are setting the Content-Type on the response coming back from amazon, but not on the response from your application
You are using a StreamReader to read the content of the stream as text and then writing it back as text
Try this instead:
using (var response = S3.GetObject(request))
{
using (var responseStream = response.ResponseStream)
{
context.Response.ContentType = "image/jpeg";
var buffer = new byte[8000];
int bytesRead = -1;
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
context.Response.OutputStream.Write(buffer, 0, bytesRead);
}
}
}
context.Response.End();

Related

Why does Stream.CanRead return false when calling GetResponseStream() second time on HttpWebResponse object

In the below sample code, streamSupportsReading is false.
private void SomeFunction()
{
HttpWebResponse responseObj = GetFile();
bool streamSupportsReading = responseObj.GetResponseStream().CanRead;
}
private HttpWebResponse GetFile()
{
var request = (HttpWebRequest)HttpWebRequest.Create("URL");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (var stream = response.GetResponseStream())
{
using (var bytes = new MemoryStream())
{
var buffer = new byte[256];
while (bytes.Length < response.ContentLength)
{
var read = stream.Read(buffer, 0, buffer.Length);
//code to calculate download percentage
}
}
}
return response;
}
It's because you put response.GetResponseStream() in a using block, so it gets disposed before GetFile() returns, making it unusable.
If you were to not dispose it, then CanRead would still be true:
private HttpWebResponse GetFile()
{
var request = (HttpWebRequest)HttpWebRequest.Create("URL");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
var stream = response.GetResponseStream();
using (var bytes = new MemoryStream())
{
var buffer = new byte[256];
while (bytes.Length < response.ContentLength)
{
var read = stream.Read(buffer, 0, buffer.Length);
//code to calculate download percentage
}
}
return response;
}

Get the file postion of each file into a stream

I am sending multiples files from my web api but I want to read each part of the stream to convert him into a byte array , then at the end I have a list of byte[], and I can save each files:
[Route("GetFiles")]
public HttpResponseMessage GetFile([FromUri] List<string> filesNames)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
if (filesNames.Count == 0)
return Request.CreateResponse(HttpStatusCode.BadRequest);
var content = new MultipartContent();
filesNames.ForEach(delegate (string fileName)
{
string filePath = System.Web.Hosting.HostingEnvironment.MapPath("~/Uploads/" + fileName);
byte[] pdf = File.ReadAllBytes(filePath);
content.Add(new ByteArrayContent(pdf));
response.Headers.Add(fileName, fileName);
});
var files = JsonConvert.SerializeObject(content);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
response = Request.CreateResponse(HttpStatusCode.OK, content);
return response;
}
Here is how I get one file into a stream, then convert him into a byte array to report the process percentage :
public static async Task<byte[]> CreateDownloadTaskForFile(string urlToDownload, IProgress<DownloadBytesProgress> progessReporter)
{
int receivedBytes = 0;
int totalBytes = 0;
WebClient client = new WebClient();
using (var stream = await client.OpenReadTaskAsync(urlToDownload))
{
byte[] buffer = new byte[BufferSize];
totalBytes = Int32.Parse(client.ResponseHeaders[HttpResponseHeader.ContentLength]);
using (MemoryStream memoryStream = new MemoryStream())
{
for (; ; )
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
await Task.Yield();
break;
}
receivedBytes += bytesRead;
if (progessReporter != null)
{
DownloadBytesProgress args = new DownloadBytesProgress(urlToDownload, receivedBytes, totalBytes);
progessReporter.Report(args);
}
}
return memoryStream.ToArray();
}
}
}
How do I get the position of a stream for each files send ?
Update :
I made a HttpResponseMessage like this :
[Route("GetFiles")]
public HttpResponseMessage GetFiles([FromUri] List<string> filesNames)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
if (filesNames.Count == 0)
return Request.CreateResponse(HttpStatusCode.BadRequest);
var content = new MultipartFormDataContent();
filesNames.ForEach(delegate (string fileName)
{
string filePath = System.Web.Hosting.HostingEnvironment.MapPath("~/Uploads/" + fileName);
byte[] pdf = File.ReadAllBytes(filePath);
content.Add(new ByteArrayContent(pdf), fileName);
});
response = Request.CreateResponse(HttpStatusCode.OK, content);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}
But from my device side : When I am trying to run the request But there is nothing on the response content :
using (var httpResponseMessage = await httpClient.GetAsync(urlToDownload + filesNamesArg))
{
var streamProvider = new MultipartMemoryStreamProvider();
streamProvider = httpResponseMessage.Content.ReadAsMultipartAsync().Result;
}
Could you show me some docs or advice ?
What?
This answer provides a 100% working example for:
Serving multiple files as a single response from a web API using multipart/mixed content type,
Reading the file contents on the client by parsing the response of the web API implemented in 1
I hope this helps.
Server:
The server application is a .Net 4.7.2 MVC project with web API support.
The following method is implemented in an ApiController and returns all the files under the ~/Uploads folder in a single response.
Please make note of the use of Request.RegisterForDispose extension to register the FileStreams for later disposal.
public async Task<HttpResponseMessage> GetFiles()
{
string filesPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Uploads");
List<string> fileNames = new List<string>(Directory.GetFiles(filesPath));
var content = new MultipartContent();
fileNames.ForEach(delegate(string fileName)
{
var fileContent = new StreamContent(File.OpenRead(fileName));
Request.RegisterForDispose(fileContent);
fileContent.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("image/jpeg");
content.Add(fileContent);
});
var response = new HttpResponseMessage();
response.Content = content;
return response;
}
The response's Content-Type header shows as Content-Type: multipart/mixed; boundary="7aeff3b4-2e97-41b2-b06f-29a8c23a7aa7" and each file is packed in different blocks separated by the boundary.
Client:
The client application is a .Net Core 3.0.1 console application.
Please note the synchronous usage of the async methods. This can be easily changed to asynchronous using await, but implemented like this for simplicity:
using System;
using System.IO;
using System.Net.Http;
namespace console
{
class Program
{
static void Main(string[] args)
{
using (HttpClient httpClient = new HttpClient())
{
using (HttpResponseMessage httpResponseMessage = httpClient.GetAsync("http://localhost:60604/api/GetImage/GetFiles").Result)
{
var content = (HttpContent)new StreamContent(httpResponseMessage.Content.ReadAsStreamAsync().Result);
content.Headers.ContentType = httpResponseMessage.Content.Headers.ContentType;
MultipartMemoryStreamProvider multipartResponse = new MultipartMemoryStreamProvider();
content.ReadAsMultipartAsync(multipartResponse);
for(int i = 0; i< multipartResponse.Contents.Count;i++)
{
Stream contentStream = multipartResponse.Contents[i].ReadAsStreamAsync().Result;
Console.WriteLine("Content {0}, length {1}", i, contentStream.Length);
}
}
}
}
}
}

How Decompress Gzipped Http Get Response in c#

Want to Decompress a Response which is GZipped Getting from an API.Tried the Below Code ,It Always return Like:-
\u001f�\b\0\0\0\0\0\0\0�Y]o........
My code is:
private string GetResponse(string sData, string sUrl)
{
try
{
string script = null;
try
{
string urlStr = #"" + sUrl + "?param=" + sData;
Uri url = new Uri(urlStr, UriKind.Absolute);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
script = reader.ReadToEnd();
}
}
catch (System.Net.Sockets.SocketException)
{
// The remote site is currently down. Try again next time.
}
catch (UriFormatException)
{
// Only valid absolute URLs are accepted
}
return script;
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
I Found the Above Code from many References for Automatic Decompression.But Eventually,it doesn't Work for me.So as to Unzip the zipped Data I tried the Below Function,
private string DecompressGZIP(string compressedText)
{
byte[] gZipBuffer = Convert.FromBase64String(compressedText);
using (var memoryStream = new MemoryStream())
{
int dataLength = BitConverter.ToInt32(gZipBuffer, 0);
memoryStream.Write(gZipBuffer, 4, gZipBuffer.Length - 4);
var buffer = new byte[dataLength];
memoryStream.Position = 0;
using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
{
gZipStream.Read(buffer, 0, buffer.Length);
}
return Encoding.UTF8.GetString(buffer);
}
}
But,it also Failed in the First Line of code itself Because of the Following Exception:
System.FormatException: 'The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. '
As i am a Beginner,Hope You guys will Guide Me .....Thanks in advance....
This is the essential bit which will take care of decoding the gzipped stream:
var clientHandler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate };
var client = new HttpClient(clientHandler);
Just Change My Function as Follows,Which is Perfectly Working For me:
private JObject PostingToPKFAndDecompress(string sData, string sUrl)
{
var jOBj = new JObject();
try
{
try
{
string urlStr = #"" + sUrl + "?param=" + sData;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlStr);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream resStream = response.GetResponseStream();
var t = ReadFully(resStream);
var y = Decompress(t);
using (var ms = new MemoryStream(y))
using (var streamReader = new StreamReader(ms))
using (var jsonReader = new JsonTextReader(streamReader))
{
jOBj = (JObject)JToken.ReadFrom(jsonReader);
}
}
catch (System.Net.Sockets.SocketException)
{
// The remote site is currently down. Try again next time.
}
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
return jOBj;
}
public static byte[] ReadFully(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
public static byte[] Decompress(byte[] data)
{
using (var compressedStream = new MemoryStream(data))
using (var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress))
using (var resultStream = new MemoryStream())
{
zipStream.CopyTo(resultStream);
return resultStream.ToArray();
}
}

Http post method hanging before getting response using c# [duplicate]

Failed to get response for large file HTTP put create file using c#
I am using file watcher service service monitor, when user created file or folder we are uploading to cloud
if file size more than 512 MB it is taking too much time to get the response
here I am confusing here the issue with my code or server
and reason for this error
if any changes on my code suggest me.
{
var fileFolderObj1 = new FileFolder();
var postURL = apiBaseUri + "/filefolder/create/file/user/" + userId; // +"?type=file";
code = HttpStatusCode.OK;
HttpWebResponse response = null;
FileInfo f = new FileInfo(filePath);
long filesizeF = f.Length;
try
{
string selectedFile = null;
selectedFile = filePath;
var fi = System.IO.Path.GetFileName(filePath);
////commented for some reason
var postParameters = new Dictionary<string, object>();
postParameters.Add("file", new FileParameter(filePath, ""));
postParameters.Add("parentId", parentId);
postParameters.Add("newName", fi);
postParameters.Add("cloudId", cloudId);
postParameters.Add("isSecure", isSecure);
//postParameters.Add("fileSize", fi.Length);
postParameters.Add("fileSize", filesizeF);
var userAgent = "Desktop";
var formDataBoundary = "----WebKitFormBoundary" + DateTime.Now.Ticks.ToString("x");
var uri = new Uri(postURL);
var createFileRequest = WebRequest.Create(uri) as HttpWebRequest;
this.SetBasicAuthHeader(createFileRequest, userId, password);
createFileRequest.ContentType = "multipart/form-data";
createFileRequest.Method = "PUT";
createFileRequest.Timeout = System.Threading.Timeout.Infinite;
createFileRequest.KeepAlive = false;/*true;*/
createFileRequest.UserAgent = userAgent;
createFileRequest.CookieContainer = new CookieContainer();
try
{
using (var requestStream = createFileRequest.GetRequestStream())
{
}
using (response = (HttpWebResponse)createFileRequest.GetResponse())
{
StreamReader(response.GetResponseStream()).ReadToEnd();
fileFolderObj1 = JsonConvert.DeserializeObject<FileFolder>(reslut);
}
}
catch (Exception exc)
{
if (response != null)
{
code = response.StatusCode;
}
}
}
catch (Exception exc)
{
}
}
}
private static readonly Encoding encoding = Encoding.UTF8;
private void WriteMultipartFormData(Dictionary<string, object> postParameters, string boundary, Stream requestStream, ILogService logService = null)
{
var needsCLRF = false;
foreach (var param in postParameters)
{
// Skip it on the first parameter, add it to subsequent parameters.
if (needsCLRF)
{
requestStream.Write(encoding.GetBytes("\r\n"), 0, encoding.GetByteCount("\r\n"));
}
needsCLRF = true;
if (param.Value is FileParameter)
{
var fileToUpload = (FileParameter)param.Value;
// Add just the first part of this param, since we will write the file data directly to the Stream
var header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n",
boundary,
param.Key,
fileToUpload.FileName ?? param.Key,
fileToUpload.ContentType ?? "application/octet-stream");
requestStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header));
// Write the file data directly to the Stream, rather than serializing it to a string.
FileStream fileStream = new FileStream(fileToUpload.FileName, FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[4096];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0,buffer.Length)) != 0)
{
requestStream.Write(buffer, 0, bytesRead);
logService.Debug("WRITEMULTIPART FORM DATA Bufferlent Running :{0}", bytesRead);
}
fileStream.Close();
}
else
{
var postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
boundary,
param.Key,
param.Value);
requestStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
}
}
// Add the end of the request. Start with a newline
var footer = "\r\n--" + boundary + "--\r\n";
requestStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));
}
}

Request relay webservice not logging in

I've made this webservice which relays a request to another server. It takes a request that is made to it's own url, then does the request to a different url and responses the result back to it's client.
void Application_BeginRequest(object sender, EventArgs e)
{
// Setup destination url schemes
string newAuth = "localhost:1861";
string newUrl = "http://" + newAuth + Request.Url.PathAndQuery;
// Setup the request from this server to the other server
HttpWebRequest newRequest = (HttpWebRequest)WebRequest.Create(newUrl);
newRequest.AllowAutoRedirect = false;
// Copy all needed headers
List<string> copyHeaderNames = new List<string>()
{
"Accept-Encoding",
"Accept-Language",
"Upgrade-Insecure-Requests",
"Cache-Control",
"Connection",
"Cookie"
};
foreach (var key in copyHeaderNames)
{
try
{
if (newRequest.Headers.AllKeys.Contains(key))
{
newRequest.Headers[key] = Request.Headers[key].Replace(Request.Url.Authority, newAuth);
}
else
{
newRequest.Headers.Add(key, Request.Headers[key].Replace(Request.Url.Authority, newAuth));
}
}
catch { }
}
// Then setup the constant paramenters of the new request
newRequest.KeepAlive = Request.Headers["Connection"] == "keep-alive";
newRequest.Accept = Request.Headers["Accept"];
newRequest.Expect = Request.Headers["Expect"];
newRequest.UserAgent = Request.Headers["User-Agent"];
newRequest.ContentType = Request.ContentType;
newRequest.Method = Request.HttpMethod;
newRequest.Host = newAuth;
newRequest.Referer = newUrl;
// If the request is a POST, I need to copy the inputstream.
if (Request.HttpMethod == "POST")
{
byte[] inputBytes = ReadToByteArray(Request.InputStream);
string inputString = System.Text.Encoding.Default.GetString(inputBytes);
// Replace original url with destination url
inputString = inputString.Replace(Request.Url.Authority, newAuth);
inputBytes = System.Text.Encoding.Default.GetBytes();
Stream reqStream = newRequest.GetRequestStream();
reqStream.Write(inputBytes, 0, inputBytes.Length);
reqStream.Close();
}
// Then do the request
using (var resp = (HttpWebResponse)newRequest.GetResponse())
{
// Setup response paramenters
Response.StatusCode = (int)resp.StatusCode;
Response.StatusDescription = resp.StatusDescription;
// Get the response stream
using (var respstream = resp.GetResponseStream())
{
var res = ReadToByteArray(respstream);
// And respond it in the current response
Response.BinaryWrite(res);
// Then I copy all response headers to the current response
foreach (var key in resp.Headers.AllKeys)
{
try
{
// Replace the destination url back to the current url
string value = resp.Headers[key].Replace(newAuth, Request.Url.Authority);
if (Response.Headers.AllKeys.Contains(key))
{
Response.Headers[key] = value;
}
else
{
Response.Headers.Add(key, value);
}
}
catch { }
}
}
}
// Tell the program to end the request.
Response.End();
}
public static byte[] ReadToByteArray(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
Now everything works except the login. The other website is a asp.net mvc4 application, it uses the standard Membership with authentication cookie.
Any ideas?
Guess copying the content of the request object doesn't exactly copy it. The solution was to go 1 layer down the OSI model to the TCP layer and do the relaying on that level.

Categories

Resources