I'm trying to send a XML file on request, but I'm getting an error when I'm trying to copy the stream, which I'm loading the file into, to the output stream.
Right now it's working fine if I'm making the request (I use HttpListener btw) from a browser; it shows me my .xml just fine. But I'd also like to be able to download the .xml when I make the request.
Any suggestions?
string xString = #"C:\Src\Capabilities.xml";
XDocument capabilities = XDocument.Load(xString);
Stream stream = response.OutputStream;
response.ContentType = "text/xml";
capabilities.Save(stream);
CopyStream(stream, response.OutputStream);
stream.Close();
public static void CopyStream(Stream input, Stream output)
{
input.CopyTo(output);
}
The error I'm getting is at input.CopyTo(output); : "Stream does not support reading."
You probably get the error because the stream input actually is the response.OutputStream, which is an output stream and also makes the source and target of the copy operation the same stream - huh?
Essentially what your code does now (and this is wrong): You save the XML content to the response's output stream (which essentially already sends it to the browser). Then you try to copy the output stream into the output stream. This doesn't work and even if it did - why? You already wrote to the output stream.
You can simplify all this greatly in my opinion as follows:
// Read the XML text into a variable - why use XDocument at all?
string xString = #"C:\Src\Capabilities.xml";
string xmlText = File.ReadAllText(xString);
// Create an UTF8 byte buffer from it (assuming UTF8 is the desired encoding)
byte[] xmlBuffer = Encoding.UTF8.GetBytes(xmlText);
// Write the UTF8 byte buffer to the response stream
Stream stream = response.OutputStream;
response.ContentType = "text/xml";
response.ContentEncoding = Encoding.UTF8;
stream.Write(xmlBuffer, 0, xmlBuffer.Length);
// Done
stream.Close();
Related
I am generating some JSON content, and then GZipping that content, before returning the gzipped content to the user, from an MVC Controller Action.
The generation of the content, and gzipping, is working correctly as I can output the generated file to disk, and then I can open that file using GZip. However, when the content is returned to the browser, the content has been corrupted.
I have tried several different approaches to returning the content to the browser, such as
return File(byte[], "application/gzip");
return new FileStreamResult(stream, "application/gzip")
And also writing directly to the Response using BinaryWrite() and WriteFile() methods
No matter what I do, the file I receive in the browser is corrupt.
This code shows the manner in which I am currently trying to return the file content.
// This line writes my content byte[] array to disk. This file when opened with gzip works fine.
System.IO.File.WriteAllBytes(#"C:\temp\test.vcp", result.FileBytes);
// Writing out the byte array to the Response results in a corrupt file. I have also attempted to Response.WriteFile(#"C:\temp\test.vcp") which also results in a corrupt file.
Response.Clear();
Response.ContentType = "application/gzip";
Response.AppendHeader("Content-Disposition", cd.ToString());
Response.AddHeader("Content-Length", result.FileBytes.Length.ToString());
Response.BinaryWrite(result.FileBytes);
Response.Flush();
Response.Close();
Response.End();
As the file I am creating can be written to disk, and can be read using Gzip, but the file received by the browser is corrupt, I am confident that my file creation is OK. But somehow after writing the file to the Response, it is being corrupted.
I did wonder if maybe some sort of HTTPHandler is manipulating the result, but I haven't added any Handlers (that I can see).
I am running the application locally currently through IISExpress. How can I check what HttpHandlers/HttpModules are being applied to the pipeline?
Ultimately I expect to receive the exact same file in my browser as is written to disk.
For reference, my generated content is 132 bytes in length, but the browser receives 216 bytes. I have noticed when looking at the byte structure of the received data, there is a repeating pattern of 3 bytes in the content, with the values 239, 191, 189. It almost looks like the resultant byte array has been stuffed or padded with these 3 bytes.
EDIT
Here is a standalone Action method which demonstrated the issue.
[HttpGet]
public void GetFile()
{
byte[] text = Encoding.ASCII.GetBytes(#"{""PetName"":""Doggy McDocFace"",""OwnerName"":""Kurt""}");
byte[] compressed = Compress(text);
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = "ExampleFile.vcp",
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = true,
};
System.IO.File.WriteAllBytes(#"C:\temp\ExampleFile.vcp", compressed);
Response.Clear();
Response.ContentType = "application/gzip";
Response.AppendHeader("Content-Disposition", cd.ToString());
Response.AddHeader("Content-Length", compressed.Length.ToString());
Response.BinaryWrite(compressed);
Response.Flush();
Response.Close();
Response.End();
}
public byte[] Compress(byte[] raw)
{
using (var memory = new MemoryStream())
{
using (var gzip = new GZipStream(memory, CompressionMode.Compress, true))
{
gzip.Write(raw, 0, raw.Length);
}
return memory.ToArray();
}
}
Here I am spoofing my JSON content, and then compressing it. The file written to disk works fine, and can be opened with my GZip application (I use 7-zip). However, the file received by the browser is corrupt. 7-zip cannot recognise it as a gzip file.
EDIT 2
So it looks like (Thanks to #Will) that the content when written to Response is falling foul of UTF-8 encoding. I cannot work out how though, as in my example above I am using Encoding.ASCII.GetBytes() to convert my string to a byte[] array.
I've tried setting the
Response.Charset = Encoding.ASCII.EncodingName;
Response.ContentEncoding = Encoding.ASCII;
But this still doesn't result in a valid file downloaded.
Edit 3
I've narrowed down the issue to the GZip encryption of the data. If I do not encrypt the data, then the plain text file downloads fine. However, encrypting the byte[] array and then writing that byte[] array to the Repsonse is resulting in what seems like UTF-8 encoding issues. Any bytes with a value over 127 are corrupted with the 3 bytes I mention further up. I cannot work out why the Response is treating this encrypted data in this way. My assumption is that when the Byte[] array is just plain text as a byte[] array, then this is handled fine. As soon as it is a proper byte[] array, i.e not just a string as a byte[] array, then some other conversion of encoding is going on in the Response.
You can try ActionFilterAttribute
Basically response filters look at the Response output stream as it's written and convert the data flowing through it.
GZip/Deflate Compression in ASP.NET MVC
public class CompressAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var encodingsAccepted = filterContext.HttpContext.Request.Headers["Accept-Encoding"];
if (string.IsNullOrEmpty(encodingsAccepted)) return;
encodingsAccepted = encodingsAccepted.ToLowerInvariant();
var response = filterContext.HttpContext.Response;
if (encodingsAccepted.Contains("deflate"))
{
response.AppendHeader("Content-encoding", "deflate");
response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
}
else if (encodingsAccepted.Contains("gzip"))
{
response.AppendHeader("Content-encoding", "gzip");
response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
}
}
}
[Compress]
[HttpGet]
public ActionResult GetFile()
{...}
I am trying to create an excel file from the response of SSRS reporting server, on front-end I successfully able to download an excel file but when I open it I get the error.
xlsx excel cannot open the file because the file format or file extension is not valid. Verify that the file has not been corrupted and that the file extension matches the format of the file.
Here is the code:
HttpWebRequest req =
(HttpWebRequest)WebRequest.Create(sTargetURL);
req.PreAuthenticate = true;
req.Credentials = new System.Net.NetworkCredential(strReportUser, strReportUserPW, strReportUserDomain);
HttpWebResponse HttpWResp = (HttpWebResponse)req.GetResponse();
Stream fStream = HttpWResp.GetResponseStream();
//Now turn around and send this as the response..
byte[] fileBytes = ReadFully(fStream);
string fileToAttach = Convert.ToBase64String(fileBytes);
HttpWResp.Close();
Stream stream = new MemoryStream(fileBytes);
result.Content = fileToAttach;
result.ContentType = "application/vnd.openxmlformats-
officedocument.spreadsheetml.sheet";
result.FileName = fileName + ".xlsx";
result.result = true;
return result;
Any help would be appreciated.
Assuming response is some kind of HTTP response object that eventually gets sent to the browser, and that you have the actual Excel file contents in fStream, the problem is you are sending the Base64'd version of the file as the contents (which Excel is not going to understand):
Stream fStream = HttpWResp.GetResponseStream();
byte[] fileBytes = ReadFully(fStream);
string fileToAttach = Convert.ToBase64String(fileBytes);
result.Content = fileToAttach;
Since I can't see what response is, all I can say is you need to leave the file contents intact.
If you want to send the raw bytes, you could do:
Stream fStream = HttpWResp.GetResponseStream();
byte[] fileBytes = ReadFully(fStream);
result.Content = fileBytes;
If your response can handle a Stream, that would be the ideal way (then you don't have to convert it back and forth between a stream and byte data).
If result.Content has to be a string, then you have to worry about which encoding to use, and it'll look something like:
Stream fStream = HttpWResp.GetResponseStream();
byte[] fileBytes = ReadFully(fStream);
result.Content = Encoding.UTF8.GetString(fileBytes);
It wouldn't surprise me if Excel needs to be encoded as UTF-16 (Microsoft uses it quite heavily internally).
I want to send a url as query string e.g.
localhost/abc.aspx?url=http:/ /www.site.com/report.pdf
and detect if the above URL returns the PDF file. If it will return PDF then it gets saved automatically otherwise it gives error.
There are some pages that uses Handler to fetch the files so in that case also I want to detect and download the same.
localhost/abc.aspx?url=http:/ /www.site.com/page.aspx?fileId=223344
The above may return a pdf file.
What is best way to capture this?
Thanks
You can download a PDF like this
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
HttpWebResponse response = req.GetResponse();
//check the filetype returned
string contentType = response.ContentType;
if(contentType!=null)
{
splitString = contentType.Split(';');
fileType = splitString[0];
}
//see if its PDF
if(fileType!=null && fileType=="application/pdf"){
Stream stream = response.GetResponseStream();
//save it
using(FileStream fileStream = File.Create(fileFullPath)){
// Initialize the bytes array with the stream length and then fill it with data
byte[] bytesInStream = new byte[stream.Length];
stream.Read(bytesInStream, 0, bytesInStream.Length);
// Use write method to write to the file specified above
fileStream.Write(bytesInStream, 0, bytesInStream.Length);
}
}
response.Close();
The fact that it may come from an .aspx handler doesn't actually matter, it's the mime returned in the server response that is used.
If you are getting a generic mime type, like application/octet-stream then you must use a more heuristical approach.
Assuming you cannot simply use the file extension (eg for .aspx), then you can copy the file to a MemoryStream first (see How to get a MemoryStream from a Stream in .NET?). Once you have a memory stream of the file, you can take a 'cheeky' peek at it (I say cheeky because it's not the correct way to parse a PDF file)
I'm not an expert on PDF format, but I believe reading the first 5 chars with an ASCII reader will yield "%PDF-", so you can identify that with
bool isPDF;
using( StreamReader srAsciiFromStream = new StreamReader(memoryStream,
System.Text.Encoding.ASCII)){
isPDF = srAsciiFromStream.ReadLine().StartsWith("%PDF-");
}
//set the memory stream back to the start so you can save the file
memoryStream.Position = 0;
I'm trying to intercept .NET Remoting Request/Responses by implementing a ServerChannelSink.
All is well, apart from the fact that I can't seem to decode the stream into a string. How do I do this?
Basically, in the watch window I can see that a value has been assigned to my variable after running the code: -
But if I open the Text Visualizer it is empty.
Similarly if I try to write the string to the Output window I don't get any lines written.
Here is the code that I'm using:
private static void PrintStream(TextWriter output, ref Stream stream)
{
// If we can't reset the stream's position after printing its content,
// we must make a copy.
if (!stream.CanSeek)
stream = CopyStream(stream);
long startPosition = stream.Position;
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
string request = enc.GetString(buffer, 0, buffer.Length);
output.WriteLine(request);
output.WriteLine();
// set stream to previous position for message processing
stream.Seek(startPosition, SeekOrigin.Begin);
}
I've also tried using a StreamReader with the same result:
private static void PrintStream(TextWriter output, ref Stream stream)
{
// If we can't reset the stream's position after printing its content,
// we must make a copy.
if (!stream.CanSeek)
stream = CopyStream(stream);
long startPosition = stream.Position;
StreamReader sr = new StreamReader(stream);
String line;
while ((line = sr.ReadLine()) != null)
{
output.WriteLine(line);
}
stream.Position = startPosition;
}
application/octet-stream means binary. Your request variable contains binary data only some of which converts to human readable text so you cannot convert it to a string.
The best you could do is use Convert.ToBase64String to convert it to base 64 but it won't be human readable. Converting it to ASCII will corrupt the data.
Thanks to paqogomez answer about \0's being interpreted as the end of the string, I just added the following: -
request = request.Replace("\0", "");
I now get this in the output window which is perfect for my purposes, thanks.
----------Request Headers-----------
__ConnectionId: 16
__IPAddress: 127.0.0.1
__RequestUri: /VisaOM.Server.ClientServices.Services
Content-Type: application/octet-stream
__CustomErrorsEnabled: False
----------Request Message-----------
get_SecurityServiceszVisaOM.Client.Services.IServices, VisaOM.Client.Services.Interfaces,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
------End of Request Message--------
I have raw data of base64Binary.
string base64BinaryStr = "J9JbWFnZ......"
How can I make pdf file? I know it need some conversion. Please help me.
Step 1 is converting from your base64 string to a byte array:
byte[] bytes = Convert.FromBase64String(base64BinaryStr);
Step 2 is saving the byte array to disk:
System.IO.FileStream stream =
new FileStream(#"C:\file.pdf", FileMode.CreateNew);
System.IO.BinaryWriter writer =
new BinaryWriter(stream);
writer.Write(bytes, 0, bytes.Length);
writer.Close();
using (System.IO.FileStream stream = System.IO.File.Create("c:\\temp\\file.pdf"))
{
System.Byte[] byteArray = System.Convert.FromBase64String(base64BinaryStr);
stream.Write(byteArray, 0, byteArray.Length);
}
First convert the Bas64 string to byte[] and write it into a file.
byte[] bytes = Convert.FromBase64String(base64BinaryStr);
File.WriteAllBytes(#"FolderPath\pdfFileName.pdf", bytes );
This code does not write any file on the hard drive.
Response.AddHeader("Content-Type", "application/pdf");
Response.AddHeader("Content-Length", base64Result.Length.ToString());
Response.AddHeader("Content-Disposition", "inline;");
Response.AddHeader("Cache-Control", "private, max-age=0, must-revalidate");
Response.AddHeader("Pragma", "public");
Response.BinaryWrite(Convert.FromBase64String(base64Result));
Note: the variable base64Result contains the Base64-String: "JVBERi0xLjMgCiXi48/TIAoxI..."
All you need to do is run it through any Base64 decoder which will take your data as a string and pass back an array of bytes. Then, simply write that file out with pdf in the file name.
Or, if you are streaming this back to a browser, simple write the bytes to the output stream, marking the appropriate mime-type in the headers.
Most languages either have built in methods for converted to/from Base64. Or a simple Google with your specific language will return numerous implementations you can use. The process of going back and forth to Base64 is pretty straightforward and can be implemented by even novice developers.
base64BinaryStr - from webservice SOAP message
byte[] bytes = Convert.FromBase64String(base64BinaryStr);