write source files to output in custom asp.net server control - asp.net

I am writing own server-side control.I need to use some images in this control.How can I write to output image file [or any other file] and then access it?
protected override void RenderContents(HtmlTextWriter output)
{
.......
output.Write("some file");
string endResult = "<img src= how to access this file? />"
output.Write(endResult);
}

Option 1: Embed It Directly
You can embed images (and other files) directly into webpages using a data URI. Something like:
protected override void RenderContents(HtmlTextWriter output)
{
............
byte[] rawImgData = []; //add your data here, maybe from a FileStream
String imgMimeType = "[img mime type, eg image/png]";
String encodedImgData = Convert.ToBase64String(rawImageData);
output.Write(String.Format("<img src=\"data:{0},{1}\" />", imageMimeType, encodedImageData))
}
As outlined in the link, this approach has lots of disadvantages. If you'll be serving the same images with every request to your control, you should really use static files.
Option 2: Save to the server and map it
Assuming your IIS worker account (normally called IUSR) has write access to some location on the server, you can save it with Server.MapPath and send the actual link out.
protected override void RenderContents(HtmlTextWriter output)
{
............
byte[] rawImgData = []; //add your data here, maybe from a FileStream
FileStream fileStream = System.IO.File.Create(Server.MapPath(virtualPath))
fileStream.Write(rawImgData, 0, rawImgData.Length)
fileStream.Close()
output.Write(String.Format("<img src=\"{0}\" />", virtualPath))
}
For repetitive requests, this definitely the best option.
Option 3: Store it in memory and serve via a second page
You can store the raw data in Session (or another temporary in-memory storage of your choice), send a link with an identifier, and server it from another page. Something like:
protected override void RenderContents(HtmlTextWriter output)
{
............
byte[] rawImgData = []; //add your data here, maybe from a FileStream
Session["MyImgTempStorage"] = rawImgData;
output.Write("<img src=\"ServeImgTempStorage.ashx?file=MyImgTempStorage\");
}
and make a generic handler called ServeImgTempStorage.ashx like this:
public class ServeImgTempStorage : System.Web.IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string fileKey = context.Request.QueryString("file");
if (string.IsNullOrEmpty(fileKey)) {
return;
}
byte[] rawData = context.Session(fileKey);
context.Response.Write(rawData);
context.Response.ContentType = "[your mime type. you can force a download with attachment;]";
context.Response.End();
}
public bool IsReusable {
get { return false; }
}
}
You'll need to make sure you use session unique identifiers for every file served in this way, or you will overwrite data.
NOTE: My C# syntax may be off, I normally write in VB.

Related

Uploading multiple HttpPostedFileBase using Parallel.ForEach breaking files

I have a form that uploads multiple files. My model has a List<HttpPostedFileBase> called SchemaFileBases, which is correctly binded. I need to upload these files to s3 and would like to do it in parallel. I'm unable to use asyc and await because this code is run from both ASP.Net and a queue based application that currently doesn't have async/await support (working on it).
If I change the foreach below to Parallel.ForEach(this.SchemaFileBases, schemaFileBase => {... Then I get some funkiness going on. The two files end up being mashed. Each file will contain some of the other files content after it's uploaded. AwsDocument is being used elsewhere in parallel so I don't think it has to do with that. Each AwsDocument has it's own AmazonS3Client.
public override void UploadToS3(IMetadataParser parser)
{
string hash;
string key;
foreach (var schemaFileBase in this.SchemaFileBases)
{
AwsDocument aws = new AwsDocument(AwsBucket.Received);
hash = schemaFileBase.InputStream.Md5Hash().ToByteArray().ToHex();
key = String.Format("{0}/{1}", this.S3Prefix, schemaFileBase.FileName);
Stream inputStream = schemaFileBase.InputStream;
aws.UploadToS3(key, inputStream, hash);
}
}
My coworker suspect's it's something to do with how the InputStream on the HttpPostedFileBase is implemented. Perhaps it is not thread safe, and the streams are both reading from the original request at the same time? I can't imagine MS would do that though.
Multi-threaded version:
public override void UploadToS3(IMetadataParser parser)
{
Parallel.ForEach(this.SchemaFileBases, f =>
{
AwsDocument aws = new AwsDocument(AwsBucket.Received);
string hash = f.InputStream.Md5Hash().ToByteArray().ToHex();
string key = String.Format("{0}/{1}", this.S3Prefix, f.FileName);
Stream inputStream = f.InputStream;
aws.UploadToS3(key, inputStream, hash);
});
}
Above solution is what I tried to multi-thread it. Does not work (files get mixed up all weird).

Web Api - How to detect when a response has finished being sent

In a web api method I am generating a file and then streaming it to the response like so
public async Task<HttpResponseMessage> GetFile() {
FileInfo file = generateFile();
var msg = Request.CreateResponse(HttpStatusCode.OK);
msg.Content = new StreamContent(file.OpenRead());
msg.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
msg.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {FileName = file.Name};
return msg;
}
because this a generated file I want to delete it after the response has finished streaming but I can't seem to find a hook in the pipeline for this.
I suppose that I can put a reference to the file in a static and set up a custom MessageHandler that pulls values out of this same static variable and deletes. However, this seems like it can't possibly be right both because of the use of a static (when this should all be per-request) and because I'd have to register a separate route.
I've seen this question but it seems to not really have much of a useful response.
Nice scenario!...the problem with using message handlers is that response writing happens at the host layers and below message handlers layer, so they are not ideal...
Following is an example of how you could do it:
msg.Content = new CustomStreamContent(generatedFilePath);
public class CustomStreamContent : StreamContent
{
string filePath;
public CustomStreamContent(string filePath)
: this(File.OpenRead(filePath))
{
this.filePath = filePath;
}
private CustomStreamContent(Stream fileStream)
: base(content: fileStream)
{
}
protected override void Dispose(bool disposing)
{
//close the file stream
base.Dispose(disposing);
try
{
File.Delete(this.filePath);
}
catch (Exception ex)
{
//log this exception somewhere so that you know something bad happened
}
}
}
By the way, are you generating this file because you are converting some data into PDF. If yes, then I think you could use PushStreamContent for this purpose by directly writing the converted data into the response stream. This way you need not generate a file first and then worry about deleting it later.
We performed same action in WebAPI. I needed to delete file just after it downloaded form server.
We can create custom response message class. It takes file path as parameter and delete it once its transmitted.
public class FileResponseMessage : HttpResponseMessage
{
private readonly string _filePath;
public FileHttpResponseMessage(string filePath)
{
this._filePath= filePath;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
Content.Dispose();
File.Delete(_filePath);
}
}
Use this class as below code and it will delete your file once it will be written on response stream.
var response = new FileResponseMessage(filePath);
response.StatusCode = HttpStatusCode.OK;
response.Content = new StreamContent(new FileStream(filePath, FileMode.Open, FileAccess.Read));
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "MyReport.pdf"
};
return response;

how to read multi part form data in .net web api controller

public class Sampleontroller:apicontroller
{
public void PostBodyMethod() {
HttpRequestMessage request=this.request;
//How to read the multi part data in the method
}
}
I am sending a multi part data to webapi controller.
How to read the contents in the method?
An 'async' example:
public async Task<HttpResponseMessage> PostSurveys()
{
// Verify that this is an HTML Form file upload request
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
//Destination folder
string uploadFolder = "mydestinationfolder";
// Create a stream provider for setting up output streams that saves the output under -uploadFolder-
// If you want full control over how the stream is saved then derive from MultipartFormDataStreamProvider and override what you need.
MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider(uploadFolder );
MultipartFileStreamProvider multipartFileStreamProvider = await Request.Content.ReadAsMultipartAsync(streamProvider);
// Get the file names.
foreach (MultipartFileData file in streamProvider.FileData)
{
//Do something awesome with the files..
}
}
Have a look at the article by Mike Wasson:
http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2
Or if you are doing file uploads, here: www.strathweb.com/2012/08/a-guide-to-asynchronous-file-uploads-in-asp-net-web-api-rtm/

Capturing SOAP requests to an ASP.NET ASMX web service

Consider the requirement to log incoming SOAP requests to an ASP.NET ASMX web service. The task is to capture the raw XML being sent to the web service.
The incoming message needs to be logged for debug inspection. The application already has its own logging library in use, so the ideal usage would be something like this:
//string or XML, it doesn't matter.
string incomingSoapRequest = GetSoapRequest();
Logger.LogMessage(incomingSoapRequest);
Are there any easy solutions to capture the raw XML of the incoming SOAP requests?
Which events would you handle to get access to this object and the relevant properties?
Is there anyway IIS can capture the incoming request and push to a log?
You can also implement by placing the code in Global.asax.cs
protected void Application_BeginRequest(object sender, EventArgs e)
{
// Create byte array to hold request bytes
byte[] inputStream = new byte[HttpContext.Current.Request.ContentLength];
// Read entire request inputstream
HttpContext.Current.Request.InputStream.Read(inputStream, 0, inputStream.Length);
//Set stream back to beginning
HttpContext.Current.Request.InputStream.Position = 0;
//Get XML request
string requestString = ASCIIEncoding.ASCII.GetString(inputStream);
}
I have a Utility method in my web service that I use to capture the request when something happens that I am not expecting like a unhandled exception.
/// <summary>
/// Captures raw XML request and writes to FailedSubmission folder.
/// </summary>
internal static void CaptureRequest()
{
const string procName = "CaptureRequest";
try
{
log.WarnFormat("{0} - Writing XML request to FailedSubmission folder", procName);
byte[] inputStream = new byte[HttpContext.Current.Request.ContentLength];
//Get current stream position so we can set it back to that after logging
Int64 currentStreamPosition = HttpContext.Current.Request.InputStream.Position;
HttpContext.Current.Request.InputStream.Position = 0;
HttpContext.Current.Request.InputStream.Read(inputStream, 0, HttpContext.Current.Request.ContentLength);
//Set back stream position to original position
HttpContext.Current.Request.InputStream.Position = currentStreamPosition;
string xml = ASCIIEncoding.ASCII.GetString(inputStream);
string fileName = Guid.NewGuid().ToString() + ".xml";
log.WarnFormat("{0} - Request being written to filename: {1}", procName, fileName);
File.WriteAllText(Configuration.FailedSubmissionsFolder + fileName, xml);
}
catch
{
}
}
Then in web.config I store several AppSetting values that define what level I want to use to capture the request.
<!-- true/false - If true will write to an XML file the raw request when any Unhandled exception occurrs -->
<add key="CaptureRequestOnUnhandledException" value="true"/>
<!-- true/false - If true will write to an XML file the raw request when any type of error is returned to the client-->
<add key="CaptureRequestOnAllFailures" value="false"/>
<!-- true/false - If true will write to an XML file the raw request for every request to the web service -->
<add key="CaptureAllRequests" value="false"/>
Then in my Application_BeginRequest I have it modified like so. Note that Configuration is a static class I create to read properties from web.config and other areas.
protected void Application_BeginRequest(object sender, EventArgs e)
{
if(Configuration.CaptureAllRequests)
{
Utility.CaptureRequest();
}
}
One way to capture the raw message is to use SoapExtensions.
An alternative to SoapExtensions is to implement IHttpModule and grab the input stream as it's coming in.
public class LogModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += this.OnBegin;
}
private void OnBegin(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpContext context = app.Context;
byte[] buffer = new byte[context.Request.InputStream.Length];
context.Request.InputStream.Read(buffer, 0, buffer.Length);
context.Request.InputStream.Position = 0;
string soapMessage = Encoding.ASCII.GetString(buffer);
// Do something with soapMessage
}
public void Dispose()
{
throw new NotImplementedException();
}
}
You know that you dont actually need to create a HttpModule right?
You can also read the contents of the Request.InputStream from within your asmx WebMethod.
Here is an article I wrote on this approach.
Code is as follows:
using System;
using System.Collections.Generic;
using System.Web;
using System.Xml;
using System.IO;
using System.Text;
using System.Web.Services;
using System.Web.Services.Protocols;
namespace SoapRequestEcho
{
[WebService(
Namespace = "http://soap.request.echo.com/",
Name = "SoapRequestEcho")]
public class EchoWebService : WebService
{
[WebMethod(Description = "Echo Soap Request")]
public XmlDocument EchoSoapRequest(int input)
{
// Initialize soap request XML
XmlDocument xmlSoapRequest = new XmlDocument();
// Get raw request body
Stream receiveStream = HttpContext.Current.Request.InputStream;
// Move to beginning of input stream and read
receiveStream.Position = 0;
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
// Load into XML document
xmlSoapRequest.Load(readStream);
}
// Return
return xmlSoapRequest;
}
}
}
There are no easy ways to do this. You will have to implement a SoapExtension. The example at the previous link shows an extension that can be used to log the data.
If you had been using WCF, then you could simply set the configuration to produce message logs.
According to Steven de Salas, you can use the Request.InputStream property within the webmethod. I have not tried this, but he says that it works.
I would want to test this with both http and https, and with and without other SoapExtensions running at the same time. These are things that might affect what kind of stream the InputStream is set to. Some streams cannot seek, for instance, which might leave you with a stream positioned after the end of the data, and which you cannot move to the beginning.

Reading an embedded file from an ASP.NET Custom Server Control an rendering it

I currently have a file "abc.htm" in my Custom Server Control Project and it's Build Action is set to Embedded Resource.
Now in the RenderContents(HtmlTextWriter output) method, I need to read that file and render it on the website.
I am trying the following but it's to no avail:
protected override void RenderContents(HtmlTextWriter output)
{
var providersURL = Page.ClientScript.GetWebResourceUrl(typeof (OpenIDSel), "OpenIDSelector.Providers.htm");
var fi = new FileInfo(providersURL); // <- exception here
//the remaining code is to possibly render the file
}
This is an example of how the providersURL is:
/WebResource.axd?d=kyU2OiYu6lwshLH4pRUCUmG-pzI4xDC1ii9u032IPWwUzMsFzFHzL3veInwslz8Y0&t=634056587753507131
FileInfo is throwing System.ArgumentException: Illegal characters in path.
You could do something like :
protected override void RenderContents(HtmlTextWriter output)
{
var source = ReadEmbeddedResource("AssemblyName", "OpenIDSelector.Providers.htm");
//the remaining code is to possibly render the file
}
private string ReadEmbeddedResource(string assemblyName, string resouceName)
{
var assembly = Assembly.Load(assemblyName);
using (var stream = assembly.GetManifestResourceStream(resouceName))
using(var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}

Resources