retrieving Binary/Blob files from Microsoft Dynamics Nav with ASP.NET - asp.net

I am working with a MS Dynamics Nav Database that have a file attachment tables. The files are stored in MS SQL. I am able to pull the files to my desktop with a custom asp.net application that I have built, but when I open the files, they are corrupted. These are PDFs files that are located in the "image" file type column of the database and I have tried to download over 20 files. All of them varies in size and seem to download successfully.
The reason why I suspect these are PDFs files is because the column right next to the binary columns give me the name of the file as in PDF format. I have also tried to renaming the file after I download to different image formats but without any luck when I tried to open it. This is not my first project to retrieve binary files, from MS SQL database. If anyone work on getting files off the Nav database before, please help me. The sample code below I wrote to retrieve files using LINQ to SQL when I give it a specific ID in the browser. Please advice me if you know any sort of compression or encryption in the binary files itself and how to grab the file successfully to read it. Thanks
protected void getFileFromID(string queryid)
{
string Filename = string.Empty;
byte[] bytes;
try
{
DataClassesFilesDataContext dcontext = new DataClassesFilesDataContext();
var myfile = (from file in dcontext.Comptroller_File_Attachments
where file.No_ == queryid
select file).First();
if (myfile.Table_ID.ToString().Length > 0 && myfile.Attachment != null)
{
Filename = myfile.FileName.ToString();
bytes = myfile.Attachment.ToArray();
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=" + Filename);
Response.BinaryWrite(bytes);
Response.End();
}
else
{
Response.Write("no file exist");
}
}
catch (Exception e)
{
Response.Write(e);
}
}

Well. I figured it out. I read on a blog that 4 bytes was the "magic number" to get rid off. So all you have to do is get rid of 4 bytes from the BLOB bytes array and then decompress it with DeflateStream. The example code I post below is an example where it takes in a byte array and skip the first 4 using LINQ-to-SQL and return the byte and string filename for the 2nd function. It also pass in a queryid string parameter. I am sure the code can be improve more for efficiency purposes. For those who have trouble with this, just give this a try.
//get bytes and remove first 4 bytes from bytes array
protected Tuple<byte[], string> getBytesfromFile(string queryID)
{
byte[] MyFilebytes = null;
string filename = string.Empty;
try
{
DataClassesFilesDataContext dcontext = new DataClassesFilesDataContext();
var myfile = (from file in dcontext.Comptroller_File_Attachments
where file.No_ == queryID
select file).First();
if (myfile.Table_ID.ToString().Length > 0 && myfile.Attachment != null)
{
MyFilebytes = myfile.Attachment.ToArray().Skip(4).ToArray();
filename = myfile.FileName.ToString();
}
else
Response.Write("no byte to return");
}
catch
{
Response.Write("no byte");
}
return Tuple.Create(MyFilebytes, filename);
}
//after getting the remaining bytes (after removing 4 first byte) deflate the byte and then store it in a memory steam and get the result back.
protected void getFile()
{
try
{
string Filename = string.Empty;
byte[] myfile = getBytesfromFile(getQueryID()).Item1;
byte[] result;
using (Stream input = new DeflateStream(new MemoryStream(myfile),
CompressionMode.Decompress))
{
using (MemoryStream output = new MemoryStream())
{
input.CopyTo(output);
result = output.ToArray();
}
}
Filename = getBytesfromFile(getQueryID()).Item2;
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=" + Filename);
Response.BinaryWrite(result);
Response.End();
}
catch (Exception e)
{
Response.Write(e);
}
}
//pass in file id
protected string getQueryID()
{
QueryID.QueryStringID = Request.QueryString["fileid"];
return QueryID.QueryStringID;
}

Related

"server cannot append header after http headers have been sent"

i have application like email messaging system. here i adjust one solution to download all file that are in table from particular post.
this is my code:
protected void lbu_download_all_Click(object sender, EventArgs e)
{
if (rpt_file_list.Items.Count > 0)
{
using (DataClassesDataContext db = new DataClassesDataContext())
{
var query = from f in db.Files
where f.Post_History_id == int.Parse(post_id.Value.ToString())
select new
{
FileName = f.File_name,
File_ext= f.File_ext
};
foreach (var item in query)
{
System.IO.FileInfo objFile = new FileInfo(Server.MapPath("~/PostFiles/" + item.FileName.ToString() + item.File_ext.ToString()));
if (objFile.Exists)
{
Response.Clear();
string strFileName = item.FileName.ToString() + item.File_ext.ToString();
Response.AddHeader("Content-Disposition", "attachment; filename=" + strFileName);
Response.AddHeader("Content-Length", objFile.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.WriteFile(objFile.FullName);
Response.BufferOutput = true;
Response.Flush();
}
}
}
}
else
{
StringBuilder sb = new StringBuilder();
sb.Append("<script type = 'text/javascript'>");
sb.Append(" No files found to download');");
sb.Append("</script>");
ClientScript.RegisterStartupScript(this.GetType(), "script", sb.ToString());
}
}
i don't know what is problm please help me..
You won't be able to download multiple files like that, I imagine what is happening is that the loop goes through once and on the second iteration it then throws the exception.
What you really should be doing is zipping all the files into one file to download, this question should give you an idea of what I mean.
By zipping the file you'll also get the benefit of compression (less bandwidth, faster transfer) and the user (in your current scenario) won't be presented with multiple 'Save As' dialog windows (much more professional!).
This link may also help you with some other potential ideas (like having a 'Download' page with URL parameters to identify the file). I'm more a fan of a zipped single file option though!
Do you have Response.BufferOutput = true; set properly? If not, the page will be sent as it is generated, which means the Response.Clear() won't do what you want :)

.NET converting file to image to store in a database

Does anyone know of a way in .NET to take an incoming stream of a file and convert it to an image to be stored in the database? (Not sure if this is possible, but wanted to check).
Edit: it is not necessarily an image stream
You need to read the stream into a byte[], then save that into the database.
You can convert the image stream to byte array and store in binary or varbinary data type in database.
Here is a short example of transferring the image into a byte array in C#:
private static byte[] ReadImage(string p_postedImageFileName, string[] p_fileType)
{
bool isValidFileType = false;
try
{
FileInfo file = new FileInfo(p_postedImageFileName);
foreach (string strExtensionType in p_fileType)
{
if (strExtensionType == file.Extension)
{
isValidFileType = true;
break;
}
}
if (isValidFileType)
{
FileStream fs = new FileStream(p_postedImageFileName, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
byte[] image = br.ReadBytes((int)fs.Length);
br.Close();
fs.Close();
return image;
}
return null;
}
catch (Exception ex)
{
throw ex;
}
}
#endregion

Insert CSV from file upload control directly to memory stream without physical path

I am not sure if this is possible so it would be nice to have some help.
What I want to do is use a fileupload control in asp.net to select a csv file. Then use my submit button on the page to run my server side code which will take that csv file and put it into memory stream where it will be parsed and then added to collection object.
I do know it's easier to save the csv file to a physical path and then do some kind of cleanup where I delete the file but if possible I would like to do it this way.
See below for code so far:
protected void btnUpload_Click(object sender, EventArgs e)
{
string connectingString = "";
if (ctrlFileUpload.HasFile)
{
string fileName =
Path.GetFileName(ctrlFileUpload.PostedFile.FileName);
string fileExtension =
Path.GetExtension(ctrlFileUpload.PostedFile.FileName);
ReadCsv(fileName);
}
}
protected void ReadCsv(string fileName)
{
// Some logic for parsing csv file in memory stream
}
}
Any ideas? Thanks!
I know this is an old question, but the below code will work for reading your posted text file into a memory stream using a StreamReader and is compatible with .NET 4.0:
protected void ReadCsv()
{
StreamReader reader = new StreamReader(ctrlFileUpload.PostedFile.InputStream);
string content = reader.ReadToEnd();
}
Note, this method is only efficient if you have enough memory on the server to handle larger files for multiple users concurrently. You don't want to use this approach if you have hundreds of users posting files simultaneously to a memory stream and causing your server to crash due to insufficient available memory. You'll also want to check if this is an acceptable method if you're on a shared hosting environment.
Does this help?
This should give you the stream. So you'd make your ReadCsv method accept a reference to the stream, and pass that to it rather than the filename, and work against the stream.
MSDN FileUpload.FileContent Property
//Try below one to capture data in MemoryStream from FileUpload Control
protected void btnFileUpload_Click(object sender, EventArgs e)
{
if (FileUploadControl.HasFile)
{
try
{
#region Capture file data in Memory Stream
byte[] fileData = null;
Stream fileStream = null;
int length = 0;
length = FileUploadControl.PostedFile.ContentLength;
fileData = new byte[length + 1];
fileStream = FileUploadControl.PostedFile.InputStream;
fileStream.Read(fileData, 0, length);
//Use MemoryStream to capture file data
MemoryStream stream = new MemoryStream(fileData);
Session["FileUploaded"] = stream;
#endregion
StreamReader strFile;
using (strFile = new StreamReader(stream))
{
string line;
DataTable dtStudentData = CreateDataTable();
DataRow drStudentRow;
List<String> errorMessages = new List<String>();
// Read and display lines from the file until the end of the file is reached.
while ((line = strFile.ReadLine()) != null)
{
if (line.Trim().Length > 0)
{
System.Threading.Thread.Sleep(1000);
string[] columns = line.Split('\t'); //splitting the line which was read by the stream reader object
Int32 charpos = (Int32)strFile.GetType().InvokeMember("charPos", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, strFile, null);
Int32 charlen = (Int32)strFile.GetType().InvokeMember("charLen",
BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.GetField
, null, strFile, null);
int lineno = (Int32)strFile.BaseStream.Position - charlen + charpos;
//Add data row in Data Table
drStudentRow = dtStudentData.NewRow();
// TO DO code - Fill data table
dtStudentData.Rows.Add(drStudentRow);
}
}
strFile.Dispose();
dtStudentData.Rows.RemoveAt(0); //Remove the first column since its the column name not necessary to insert in the database table
PopulateStudentInvalidDataGridView(dtStudentData); // Bind Grid
Session["StudentData_FileParsedStudentRegistrtaionTable"] = dtStudentData;
strFile.Close(); //release the stream reader
}
}
catch (Exception ex)
{
String error = ex.Message;
}
}
}

Remote file Download via ASP.NET corrupted file

I am using the code below which I have found on one of the forums to download a file in remote server. it seems it is working. However, the downloaded file is corrupted and I cannot unzip.
Do you have any idea why it is so? or if my approach is wrong, could you suggest me a better way please?
protected void Page_Load(object sender, EventArgs e)
{
string url = "http://server/scripts/isynch.dll?panel=AttachmentDownload&NoteSystem=SyncNotes&NoteType=Ticket&NoteId=1&Field=supp&File=DisplayList%2etxt";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Credentials = new NetworkCredential("user", "pass");
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
////Initialize the output stream
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AppendHeader("Content-Disposition:", "attachment; filename=" + "DisplayList.txt");
Response.AppendHeader("Content-Length", resp.ContentLength.ToString());
////Populate the output stream
byte[] ByteBuffer = new byte[resp.ContentLength];
Stream rs = req.GetResponse().GetResponseStream();
rs.Read(ByteBuffer, 0, ByteBuffer.Length);
Response.BinaryWrite(ByteBuffer);
Response.Flush();
///Cleanup
Response.End();
rs.Dispose();
}
First of all, use application/octet-stream as it is the standard content type for downloads.
new byte[resp.ContentLength + 1] will define a buffer which is one byte larger than content type. I believe this is the reason for corruption. Use new byte[resp.ContentLength].
I actually recommend re-writing it and removing memorystream:
const int BufferLength = 4096;
byte[] byteBuffer = new byte[BufferLength];
Stream rs = req.GetResponse().GetResponseStream();
int len = 0;
while ( (len = rs.Read(byteBuffer,0,byteBuffer.Length))>0)
{
if (len < BufferLength)
{
Response.BinaryWrite(byteBuffer.Take(len).ToArray());
}
else
{
Response.BinaryWrite(byteBuffer);
}
Response.Flush();
}
the article on http://support.microsoft.com/default.aspx?scid=kb;en-us;812406 solved my problem. Many thanks to #Aliostad for his effort to help me.

Streaming a zip file over http in .net with SharpZipLib

I'm making a simple download service so a user can download all his images from out site.
To do that i just zip everything to the http stream.
However it seems everything is stored in memory, and the data isn't sent til zip file is complete and the output closed.
I want the service to start sending at once, and not use too much memory.
public void ProcessRequest(HttpContext context)
{
List<string> fileNames = GetFileNames();
context.Response.ContentType = "application/x-zip-compressed";
context.Response.AppendHeader("content-disposition", "attachment; filename=files.zip");
context.Response.ContentEncoding = Encoding.Default;
context.Response.Charset = "";
byte[] buffer = new byte[1024 * 8];
using (ICSharpCode.SharpZipLib.Zip.ZipOutputStream zipOutput = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(context.Response.OutputStream))
{
foreach (string fileName in fileNames)
{
ICSharpCode.SharpZipLib.Zip.ZipEntry zipEntry = new ICSharpCode.SharpZipLib.Zip.ZipEntry(fileName);
zipOutput.PutNextEntry(zipEntry);
using (var fread = System.IO.File.OpenRead(fileName))
{
ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(fread, zipOutput, buffer);
}
}
zipOutput.Finish();
}
context.Response.Flush();
context.Response.End();
}
I can see the the worker process memory growing while it makes the file, and then releases the memory when its done sending. How do i do this without using too much memory?
Disable response buffering with context.Response.BufferOutput = false; and remove the Flush call from the end of your code.
use Response.BufferOutput = false; at start of ProcessRequest and flush response after each file.
FYI. This is working code to recursively add an entire tree of files, with streaming to browser:
string path = #"c:\files";
Response.Clear();
Response.ContentType = "application/zip";
Response.AddHeader("Content-Disposition", string.Format("attachment; filename=\"{0}\"", "hive.zip"));
Response.BufferOutput = false;
byte[] buffer = new byte[1024 * 1024];
using (ZipOutputStream zo = new ZipOutputStream(Response.OutputStream, 1024 * 1024)) {
zo.SetLevel(0);
DirectoryInfo di = new DirectoryInfo(path);
foreach (string file in Directory.GetFiles(di.FullName, "*.*", SearchOption.AllDirectories)) {
string folder = Path.GetDirectoryName(file);
if (folder.Length > di.FullName.Length) {
folder = folder.Substring(di.FullName.Length).Trim('\\') + #"\";
} else {
folder = string.Empty;
}
zo.PutNextEntry(new ZipEntry(folder + Path.GetFileName(file)));
using (FileStream fs = File.OpenRead(file)) {
ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(fs, zo, buffer);
}
zo.Flush();
Response.Flush();
}
zo.Finish();
}
Response.Flush();

Resources