I have successfully uploaded files into my SQL Server database. I can bring back the information into a GridView. I am unable to figure out how to create a hyperlink to actually open the file.
You need to create an URL that handles the pictures and returns the DB content into the response stream. As it happens at SQL Saturday #26 I had a presentation that showed exactly this. You can doaloand my slides from the link, go into Demo 2 and in the lotsOfPictures solution you'll find Picture.aspx.cs, that does exactly what you ask for:
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand cmd = new SqlCommand(
#"SELECT picture
FROM resized_pictures
WHERE picture_id = #id
AND picture_size = #size;", conn);
cmd.Parameters.AddWithValue("#id", pictureId);
cmd.Parameters.AddWithValue("#size", size);
using (SqlDataReader rdr = cmd.ExecuteReader(
CommandBehavior.SequentialAccess))
{
if (rdr.Read())
{
Response.ContentType = "image/jpeg";
byte[] bytes = new byte[1024];
long offSet = 0;
int countRead = (int) rdr.GetBytes(
0, offSet, bytes, 0, 1024);
while (countRead > 0)
{
Response.OutputStream.Write(bytes, 0, countRead);
offSet += countRead;
countRead = (int)rdr.GetBytes(
0, offSet, bytes, 0, 1024);
}
}
}
}
The important pieces of the puzzle are the SequentialAccess flag passed to the SqlCommand reader that will return a true stream, so the page does not load the whole image in memory before returnning. For a high performance server you should use async operations, like described in Asynchronous Pages in ASP.NET 2.0.
Have not tried this myself, but how about streaming the data into a temporary file on the local file system and then providing a link to that temporary file?
Normally you would use your server side code to send the appropriate headers, then just echo the content.
From the PHP manual:
// We'll be outputting a PDF
header('Content-type: application/pdf');
// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');
// The PDF source is in original.pdf
readfile('original.pdf');
If your file is stored in the database, you are probably storing it as a blog or byte array. On the hyperlink click event, you will need to pass the byte array into a stream, and use a stream writer to create the file. Then execute the file.
Related
I have a filename pointing to a text file, including its path, as a string. Now I'd like to load this .csv file into memory stream. How should I do that?
For example, I have this:
Dim filename as string="C:\Users\Desktop\abc.csv"
Dim stream As New MemoryStream(File.ReadAllBytes(filename))
You don't need to load a file into a MemoryStream.
You can simply call File.OpenRead to get a FileStream containing the file.
If you really want the file to be in a MemoryStream, you can call CopyTo to copy the FileStream to a MemoryStream.
I had an XML file being read from disk, using the old XmlReader API. How to read the XML file into memory, and then work with it in memory, instead of reading the disk repeatedly? Based on VB answer from Centro (upvoted) but with a Using block, and in C#.
The key line:
MemoryStream myXMLDocument = new MemoryStream(File.ReadAllBytes(#"c:\temp\myDemoXMLDocument.xml"));
Re the OP's question, if you wanted to load a CSV file into a MemoryStream:
MemoryStream myCSVDataInMemory = new MemoryStream(File.ReadAllBytes(#"C:\Users\Desktop\abc.csv"));
Following is a code snippet showing code to reads through XML document now that it's in a MemoryStream. Basically the same code as when it was coming from a FileStream that pointed to a file on disk. Yes, the XMLTextReader API is old and clunky, but it's what I had to work with in this app.
string myXMLFileName = #"c:\temp\myDemoXMLDocument.xml";
using (MemoryStream myXMLDocument = new MemoryStream(File.ReadAllBytes(myXMLFileName)))
{
myXMLTextReader = new XmlTextReader(myXMLDocument);
myXMLTextReader.WhitespaceHandling = WhitespaceHandling.None;
myXmlTextReader.Read(); // read the XML declaration node, advance to <Batch> tag
while (!myXmlTextReader.EOF)
{
if (myXmlTextReader.Name == "xml" && !myXmlTextReader.IsStartElement()) break;
// advance to <Batch> tag
while (myXmlTextReader.Name == "Batch" && myXmlTextReader.IsStartElement())
{
string BatchIdentifier = myXmlTextReader.GetAttribute("BatchIdentifier");
myXmlTextReader.Read(); // advance to next tag
while (!myXmlTextReader.EOF)
{
if (myXmlTextReader.Name == "Transaction" && myXmlTextReader.IsStartElement())
{
// Start a new set of items
string transactionID = myXmlTextReader.GetAttribute("ID");
myXmlTextReader.Read(); // Read next element, possibly another Transaction tag
}
}
//All Batch tags are completed.Move to next tag
myXmlTextReader.Read();
}
// Close the XML memory stream.
myXmlTextReader.Close();
myXmlDocument.Close();
}
}
You can copy it to a file stream like so:
string fullPath = Path.Combine(filePath, fileName);
FileStream fileStream = new FileStream(fullPath, FileMode.Open);
Image image = Image.FromStream(fileStream);
MemoryStream memoryStream = new MemoryStream();
image.Save(memoryStream, ImageFormat.Jpeg);
//Close File Stream
fileStream.Close();
I am having trouble merging PDFs in-memory. I have 2 memory streams, a master and component stream, the idea is that as each component PDF is built up, the component PDF's bytes are added to the master stream. At the very end of all the components, we have a byte array that's a PDF.
I have the code below, but nothing is copying into my masterStream. I think the issue is with CopyPagesTo, but I'm not familiar enough and the documentation/examples are hard to find.
byte[] updated;
using (MemoryStream masterMemoryStream = new MemoryStream())
{
masterStream.WriteTo(masterMemoryStream);
// Read from master stream (ie. all existing components)
masterMemoryStream.Position = 0;
using (iText.Kernel.Pdf.PdfWriter masterPdfWriter = new iText.Kernel.Pdf.PdfWriter(masterMemoryStream))
using (iText.Kernel.Pdf.PdfDocument masterPdfDocument = new iText.Kernel.Pdf.PdfDocument(masterPdfWriter))
{
using (MemoryStream componentMemoryStream = new MemoryStream())
{
componentStream.WriteTo(componentMemoryStream);
// Read from new component
componentMemoryStream.Position = 0;
using (iText.Kernel.Pdf.PdfReader componentPdfReader = new iText.Kernel.Pdf.PdfReader(componentMemoryStream))
using (iText.Kernel.Pdf.PdfDocument componentPdfDocument = new iText.Kernel.Pdf.PdfDocument(componentPdfReader))
{
// Copy pages from component into master
componentPdfDocument.CopyPagesTo(1, componentPdfDocument.GetNumberOfPages(), masterPdfDocument);
}
}
}
updated = masterMemoryStream.GetBuffer();
}
// Write updates to master stream?
masterStream.SetLength(0);
using (MemoryStream temp = new MemoryStream(updated))
temp.WriteTo(masterStream);
Answer
This is mkl's answer with some of my corrections:
using (MemoryStream temporaryStream = new MemoryStream())
{
masterStream.Position = 0;
componentStream.Position = 0;
using (PdfDocument combinedDocument = new PdfDocument(new PdfReader(masterStream), new PdfWriter(temporaryStream)))
using (PdfDocument componentDocument = new PdfDocument(new PdfReader(componentStream)))
{
componentDocument.CopyPagesTo(1, componentDocument.GetNumberOfPages(), combinedDocument);
}
byte[] temporaryBytes = temporaryStream.ToArray();
masterStream.Position = 0;
masterStream.SetLength(temporaryBytes.Length);
masterStream.Capacity = temporaryBytes.Length;
masterStream.Write(temporaryBytes, 0, temporaryBytes.Length);
}
There are a number of issues in your code. I'll first give you a working version and then go into the issues in your code.
A working version (with an important limitation)
You can combine two PDFs given in MemoryStream instances masterStream and componentStream and get the result in the same MemoryStream instance masterStream as follows:
using (MemoryStream temporaryStream = new MemoryStream())
{
masterStream.Position = 0;
componentStream.Position = 0;
using (PdfDocument combinedDocument = new PdfDocument(new PdfReader(masterStream), new PdfWriter(temporaryStream)))
using (PdfDocument componentDocument = new PdfDocument(new PdfReader(componentStream)))
{
componentDocument.CopyPagesTo(1, componentDocument.GetNumberOfPages(), combinedDocument);
}
byte[] temporaryBytes = temporaryStream.ToArray();
masterStream.Position = 0;
masterStream.Capacity = temporaryBytes.Length;
masterStream.Write(temporaryBytes, 0, temporaryBytes.Length);
masterStream.Position = 0;
}
The limitation is that you have to have instantiated the masterStream with an expandable capacity; the MemoryStream class has a number of constructors only some of which create such an expandable instance while the others create non-resizable instances. For details read here.
Issues in your concept and code
Concatenating PDF files does not result in a valid merged PDF
You describe your concept like this
the idea is that as each component PDF is built up, the component PDF's bytes are added to the master stream
This does not work, though, the PDF format does not allow merging PDFs by simply concatenating them. In particular the (active) objects in a PDF have an identifier number which must be unique in the PDF, concatenating would result in a file with non-unique object identifiers; PDFs contain cross reference structures which map each object identifier to its offset from the file start, concatenating would get all these offsets wrong for the added PDFs; furthermore, a PDF has to have a single root object from which the other objects are referenced directly or indirectly, concatenating would result in multiple root objects.
Writing and immediately overwriting
In your code you have
masterStream.WriteTo(masterMemoryStream);
// Read from master stream (ie. all existing components)
masterMemoryStream.Position = 0;
using (iText.Kernel.Pdf.PdfWriter masterPdfWriter = new iText.Kernel.Pdf.PdfWriter(masterMemoryStream))
Here you write the contents of masterStream to masterMemoryStream, then set the masterMemoryStream position to the start and instantiate a PdfWriter which starts writing there. I.e. your original copy of the masterStream contents get overwritten, surely not what you wanted.
Using MemoryStream.GetBuffer
MemoryStream.GetBuffer does not only return the data written into the MemoryStream by design but the whole buffer; i.e. there may be a lot of trash bytes after the actual PDF in what you retrieve here
updated = masterMemoryStream.GetBuffer();
This may cause PDF processors trying to process your result PDFs to be unable to open the file: PDFs have a pointer to the last cross references at their end, so if you have trash bytes following the actual end of your PDF, PDF processors may not find that pointer.
PS
As worked out in the comments, the code above works fine in case of constantly growing stream lengths (which usually will happen in the use case at hand) but in general one needs to restrict the stream size before writing the new content, e.g. like this:
...
masterStream.Position = 0;
masterStream.SetLength(temporaryBytes.Length); // <<<<
masterStream.Capacity = temporaryBytes.Length;
...
I have a method that uses Microsoft.Office.Interop to read a Word document, edit it, and write it as a PDF. I have a separate method that uses Itext7 to read this PDF and write it to a different PDF that can be viewed and printed, but cannot be easily altered.
The first method reads the word document from disk; however, I've been asked to make it read the document from an sql query from a variable stored as varbinary and write the final result as a varbinary -without using any intermediate files on disk. I think I need to read these as "streams"
Here's what I have:
class clsMakeCert
{
string myName;
public string myDate;
public clsMakeCert(string name, DateTime date)
{
myName = name;
myDate = date.ToString("MM/dd/yyyy");
}
public void createCertPdf(string certFilename)
{
// Get the document out of the SQL table
System.Data.DataTable dtContents = new System.Data.DataTable();
SqlDataReader rdr_Contents = null;
using (SqlConnection conn = new SqlConnection("Server=KGREEN3-LT\\SQLEXPRESS;Initial Catalog=SAN;Integrated Security=SSPI"))
{
conn.Open();
SqlCommand cmd = new SqlCommand("Select [file_data] From cert_files where filename=#certFilename", conn);
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#certFilename", certFilename);
rdr_Contents = cmd.ExecuteReader(CommandBehavior.CloseConnection);
dtContents.Load(rdr_Contents);
}
}
byte[] byteArray = (byte[])dtContents.Rows[0]["file_data"];
// Put it into a word document
Application wordApp = new Application { Visible = false };
Document doc = new Document();
using (MemoryStream stream = new MemoryStream())
{
stream.Write(byteArray, 0, (int)byteArray.Length);
doc = wordApp.Documents.Open(stream, ReadOnly: false, Visible: false);
doc.Activate();
FindAndReplace(wordApp, "First Middle Last", myName);
FindAndReplace(wordApp, "11/11/1111", myDate);
}
return ;
}
}
It doesn't look like the open method will accept a stream, though.
It looks like I can use openXML to read / edit the docx as a stream. But it won't convert to pdf.
It looks like I can use Interop to read /edit the docx AND write to PDF, but I can't do it as a stream (only as a file on disk).
Is there a way to get Interop to read a stream (i.e. a file loaded from a varbinary)?
k
No, the Word "Interop" does not support streaming. The closest it comes (and it's the only Office application that has this capability) is to pass in the necessary Word Open XML in the OPC flat file format using the obejct model's InsertXML method. It's not guaranteed, however, that the result will be an exact duplicate of the Word Open XML file being passed in as the target document's settings could override some of the in-coming settings.
I have a URL to MP4 audio file that I need to send to Speech-To-Text API. The API accepts only WAV stream. I am using NAudio 1.7.3 and the following code to download the file and to get the appropriate stream to be sent to API:
string filePath = "C:\Windows\Temp\file.wav";
using (MediaFoundationReader reader = new MediaFoundationReader(audioFileURL))
{
WaveFileWriter.CreateWaveFile(filePath, reader);
}
System.IO.FileStream fs = new FileStream(filePath, FileMode.Open);
Then I send the fs stream to API and everything works correctly, although very slowly because of I/O to/from disk.
I decided to rewrite this code and execute all required in memory. For this purpose I wrote the following code (that does not provide me a correct stream):
using (MediaFoundationReader reader = new MediaFoundationReader(audioLocation)){
MemoryStream ms = new MemoryStream();
IgnoreDisposeStream ids = new IgnoreDisposeStream(ms);
WaveFileWriter writer = new WaveFileWriter(ids, reader.WaveFormat);
//Doing one of the following (both provide the same outcome):
//1. reader.CopyTo(ids);
//or
//2. this code from NAudio source:
var buffer = new byte[reader.WaveFormat.AverageBytesPerSecond * 4];
while (true)
{
int bytesRead = reader.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
// end of source provider
break;
}
// Write will throw exception if WAV file becomes too large
writer.Write(buffer, 0, bytesRead);
}
writer.Dispose();
Stream streamToSendToAPI = ids.SourceStream;
//Send streamToSendToAPI to Speech-To-Text API
}
My expectation is that using second code example, where I create stream with WAV header and then add the data to the stream, would provide me a valid WAV stream. However, when I send it to speech-to-text API, the API gives error that indicates that stream cannot be processed (meaning that stream is invalid).
Please advise how to fix the in-memory code example to create a valid WAV stream
You need to rewind the memory stream back to the beginning
ms.Position = 0
I am first time trying to use filestream to store pdf files on file system using varbinary(MAX) column type of DB.
I have followed following steps.
enabled filestream feature on SQL server 2008 R2.
Create a filegroup for BLOB storage
created table with blob column of type varbinary(max)
Now, I want to use file upload control to select file and when click on upload button it should save the pdf file. Also, how to retrieve the file?
I have tried following code
protected void btnFSupload_Click(object sender, EventArgs e)
{
SqlConnection cn = null;
SqlTransaction tx = null;
SqlCommand cmd = null;
SqlCommand cmd2 = null;
bool bCommit = false;
try
{
// read in the file to be saved as a blob in the database
FileStream input = new FileStream(#"D:\swami.pdf", FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[(int)input.Length];
input.Read(buffer, 0, buffer.Length);
cn = new SqlConnection("server=at-hetal01\\sqlexpress;Initial Catalog=practice;Integrated Security=true;");
cn.Open();
tx = cn.BeginTransaction();
cmd = new SqlCommand("dbo.stp_AddBLOB", cn, tx);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlDataReader r = cmd.ExecuteReader(System.Data.CommandBehavior.SingleRow);
r.Read();
string id = r[0].ToString();
string path = r[1].ToString();
r.Close();
// get the transaction context
cmd2 = new SqlCommand("SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()", cn, tx);
Object obj = cmd2.ExecuteScalar();
byte[] txCtx = (byte[])obj;
// open the filestream to the blob
SafeFileHandle handle = OpenSqlFilestream(path,DESIRED_ACCESS_WRITE,SQL_FILESTREAM_OPEN_NO_FLAGS,txCtx,(UInt32)txCtx.Length,0);
// open a Filestream to write the blob
FileStream output = new FileStream(handle,FileAccess.Write,buffer.Length,false);
output.Write(buffer,0,buffer.Length);
output.Close();
if (handle != null && !handle.IsClosed)
handle.Close();
bCommit = true;
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
finally
{
if (cn != null)
{
switch (bCommit)
{
case true:
tx.Commit();
break;
case false:
tx.Rollback();
break;
}
cn.Close();
}
}
}
Above code shows error as below
The operating system returned the error '0xc000003a({Path Not Found} The path %hs does not exist.)' while attempting 'NtCreateFile' on 'D:\DB\FS\d11132f8-c2a8-452d-ae0c-208164a550d7\beb8e1f1-8116-440b-870b-7cef4281a15d\0000001c-000000e4-010d'. The statement has been terminated.
So, any clue on this?
If you have altered your table using SSMS table designer, the FILESTEAM column attribute will be lost producing the path not found. Make sure the FILESTREAM attribute is set for the file field by running the follwoing statement in your database:
select SERVERPROPERTY('FilestreamShareName') as ShareName,
SERVERPROPERTY('FilestreamConfiguredLevel') as ConfiguredLevel,
SERVERPROPERTY('FilestreamEffectiveLevel') as EffectiveLevel
You'll need to alter the table via a script and NOT SSMS to tie your varchar(max)/filestream field to the FileGroup you should have already created.
When I ran into this issue, I found the answer on StackOverflow, but can't seem to find it again for the reference.
I know this is old, but for future reference:
We checked the SERVERPROPERTY values that #BMP suggested. They were configured correctly, so that didn't help.
However, we went ahead and turned OFF the windows file share part of the file streaming access. Once this was done, the error went away.
In our case it was a web app running on the exact same machine as the sql server which exhibited the problem. I'm not sure if the web app's app pool user didn't have access to the file share created by windows or not.
The details were:
Windows 2003 Server (x86)
IIS 6
SQL Server 2008 R2 Express
UPDATE: Apparently this worked for a few days. It's not working any more.