Download a .zip file from database using C# Ado.net - c#

I have a .zip file in the database (BLOB). I want to retrieve the same in my api. Please find below code snippet. I am able to download a zip file but not able to extract it. getting an error while extracting.
public IHttpActionResult GetDownloadLetter()
{
DownloadDocument docInfo = blogicObj.DownloadLetter();
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(docInfo.Document.GetBuffer())
};
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = docInfo.DocumentName + ".zip"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
var response = ResponseMessage(result);
return response;
}
public class DownloadDocument
{
public MemoryStream Document { get; set; }
public string DocumentType { get; set; }
public string DocumentName { get; set; }
}
public DownloadDocument DownloadDocument()
{
DownloadDocument letter = null;
try
{
letter = GetDummyDownload();
}
catch (Exception ex)
{
throw ex;
}
return letter;
}
public MemoryStream GetMemoryStream(SqlDataReader reader, int columnIndex)
{
using (MemoryStream stream = new MemoryStream())
{
long startIdx = 0;
byte[] buffer = new byte[256];
while (true)
{
long retBytes = reader.GetBytes(columnIndex, startIdx, buffer, 0, buffer.Length);
stream.Write(buffer, 0, (int)retBytes);
startIdx += retBytes;
if (retBytes != buffer.Length)
break;
}
return stream;
}
}
public DownloadDocument GetDummyDownload()
{
DownloadDocument letter = null;
string strQuery = "select * from [dbo].[documents] where id=1";
using (SqlConnection connection = new SqlConnection(connStr))
{
SqlCommand command = new SqlCommand(connStr);
command.CommandType = CommandType.Text;
command.CommandText = strQuery;
command.Connection = connection;
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
letter = new DownloadDocument();
letter.Document = GetMemoryStream(reader, 4); //4 is for document column
letter.DocumentType = Convert.ToString(reader["DocumentType"]);
letter.DocumentName = Convert.ToString(reader["Name"]);
}
// Call Close when done reading.
reader.Close();
}
return letter;
}

Your C# application can use J# library to extract zip file. It's basically Java library:
using(var fis = new java.io.FileInputStream(FileName))
{
using(var zis = new java.util.zip.ZipInputStream(fis))
{
java.util.zip.ZipEntry ze;
while((ze = zis.getNextEntry()) != null)
{
if (ze.isDirectory())
continue;
Console.WriteLine("File name: " + ze.getName());
}
}
}

public IHttpActionResult GetDownloadLetter()
{
DownloadDocument docInfo = blogicObj.DownloadLetter();
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(docInfo.Document.ToArray())
};
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = docInfo.DocumentName + ".zip"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
var response = ResponseMessage(result);
return response;
}
public class DownloadDocument
{
public MemoryStream Document { get; set; }
public string DocumentType { get; set; }
public string DocumentName { get; set; }
}
public DownloadDocument DownloadDocument()
{
DownloadDocument letter = null;
try
{
letter = GetDummyDownload();
}
catch (Exception ex)
{
throw ex;
}
return letter;
}
public MemoryStream GetMemoryStream(SqlDataReader reader, int columnIndex)
{
byte[] buffer = (byte[])reader.GetValue(columnIndex);
MemoryStream stream = new MemoryStream(buffer);
return stream;
}
public DownloadDocument GetDummyDownload()
{
DownloadDocument letter = null;
string strQuery = "select * from [dbo].[documents] where id=1";
using (SqlConnection connection = new SqlConnection(connStr))
{
SqlCommand command = new SqlCommand(connStr);
command.CommandType = CommandType.Text;
command.CommandText = strQuery;
command.Connection = connection;
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
letter = new DownloadDocument();
letter.Document = GetMemoryStream(reader, 4); //4 is for document column
letter.DocumentType = Convert.ToString(reader["DocumentType"]);
letter.DocumentName = Convert.ToString(reader["Name"]);
}
// Call Close when done reading.
reader.Close();
}
return letter;
}

Related

Decrypt Google Cookies in c# .Net Framework?

I've created this, and it's able to get the cookies from Google Chrome when given a specific domain name. However, the values are decrypted. I know there must be a way I can modify my code to decrypt these values.
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Test
{
public class Test
{
public List<Data> GetCookies(string hostname)
{
List<Data> data = new List<Data>();
if (ChromeCookiesExists())
{
try
{
using (var conn = new SqliteConnection($"Data Source={ChromeCookiePath}"))
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = $"SELECT name,encrypted_value,host_key FROM cookies WHERE host_key = '{hostname}'";
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
if (!data.Any(a => a.Name == reader.GetString(0)))
{
data.Add(new Data()
{
Name = reader.GetString(0),
Value = reader.GetString(1) //HERE is my problem because this returns encrypted value not decrypted
});
}
}
}
conn.Close();
}
}
catch { }
}
return data;
}
private string ChromeCookiePath = #"C:\Users\" + Environment.UserName + #"\AppData\Local\Google\Chrome\User Data\Default\Cookies";
private bool ChromeCookiesExists()
{
if (File.Exists(ChromeCookiePath))
return true;
return false;
}
public class Data
{
public string Name { get; set; }
public string Value { get; set; }
}
}
}
This code outputs a struct called Data which contains the name and the value of the cookie (just not decrypted atm).
Thanks to Topaco's comment and UnCavoHDMI, I was able to put this together.
class ChromeManager
{
public List<Cookie> GetCookies(string hostname)
{
string ChromeCookiePath = #"C:\Users\" + Environment.UserName + #"\AppData\Local\Google\Chrome\User Data\Default\Cookies";
List<Cookie> data = new List<Cookie>();
if (File.Exists(ChromeCookiePath))
{
try
{
using (var conn = new SqliteConnection($"Data Source={ChromeCookiePath}"))
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = $"SELECT name,encrypted_value,host_key FROM cookies WHERE host_key = '{hostname}'";
byte[] key = AesGcm256.GetKey();
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
if (!data.Any(a => a.Name == reader.GetString(0)))
{
byte[] encryptedData = GetBytes(reader, 1);
byte[] nonce, ciphertextTag;
AesGcm256.prepare(encryptedData, out nonce, out ciphertextTag);
string value = AesGcm256.decrypt(ciphertextTag, key, nonce);
data.Add(new Cookie()
{
Name = reader.GetString(0),
Value = value
});
}
}
}
conn.Close();
}
}
catch { }
}
return data;
}
private byte[] GetBytes(SqliteDataReader reader, int columnIndex)
{
const int CHUNK_SIZE = 2 * 1024;
byte[] buffer = new byte[CHUNK_SIZE];
long bytesRead;
long fieldOffset = 0;
using (MemoryStream stream = new MemoryStream())
{
while ((bytesRead = reader.GetBytes(columnIndex, fieldOffset, buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, (int)bytesRead);
fieldOffset += bytesRead;
}
return stream.ToArray();
}
}
public class Cookie
{
public string Name { get; set; }
public string Value { get; set; }
}
class AesGcm256
{
public static byte[] GetKey()
{
string sR = string.Empty;
var appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string path = #"C:\Users\" + Environment.UserName + #"\AppData\Local\Google\Chrome\User Data\Local State";
string v = File.ReadAllText(path);
dynamic json = JsonConvert.DeserializeObject(v);
string key = json.os_crypt.encrypted_key;
byte[] src = Convert.FromBase64String(key);
byte[] encryptedKey = src.Skip(5).ToArray();
byte[] decryptedKey = ProtectedData.Unprotect(encryptedKey, null, DataProtectionScope.CurrentUser);
return decryptedKey;
}
public static string decrypt(byte[] encryptedBytes, byte[] key, byte[] iv)
{
string sR = String.Empty;
try
{
GcmBlockCipher cipher = new GcmBlockCipher(new AesEngine());
AeadParameters parameters = new AeadParameters(new KeyParameter(key), 128, iv, null);
cipher.Init(false, parameters);
byte[] plainBytes = new byte[cipher.GetOutputSize(encryptedBytes.Length)];
Int32 retLen = cipher.ProcessBytes(encryptedBytes, 0, encryptedBytes.Length, plainBytes, 0);
cipher.DoFinal(plainBytes, retLen);
sR = Encoding.UTF8.GetString(plainBytes).TrimEnd("\r\n\0".ToCharArray());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
return sR;
}
public static void prepare(byte[] encryptedData, out byte[] nonce, out byte[] ciphertextTag)
{
nonce = new byte[12];
ciphertextTag = new byte[encryptedData.Length - 3 - nonce.Length];
System.Array.Copy(encryptedData, 3, nonce, 0, nonce.Length);
System.Array.Copy(encryptedData, 3 + nonce.Length, ciphertextTag, 0, ciphertextTag.Length);
}
}
}

Why is my image (from database) not displaying properly in my pictureBox?

I save my image like this:
//This is in my ImageConverter class:
public static byte[] ConvertImageToByteArray(Image userImage) //Get bytes of the image
{
using (MemoryStream ms = new MemoryStream())
using (Bitmap tempImage = new Bitmap(userImage))
{
tempImage.Save(ms, userImage.RawFormat);
return ms.ToArray();
}
}
//this is in my save button:
sqlCmd.Parameters.Add("#user_image", SqlDbType.VarBinary, 8000).Value =
ImageConverter.ConvertImageToByteArray(pictureBox1.Image);
I retrieve my image by clicking on the datagridview like this:
private void dgvEmpDetails_CellClick(object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.RowIndex != -1)
{
//Display user image
using (SqlConnection con = new SqlConnection(connectionStringConfig))
using (SqlCommand sqlCmd = new SqlCommand(
"SELECT user_image FROM dbo.Employee_Image
WHERE employee_id=#employee_id", con))
{
con.Open();
sqlCmd.Parameters.Add("#employee_id",
SqlDbType.NVarChar).Value = EmployeeId;
using (SqlDataReader reader = sqlCmd.ExecuteReader())
{
if (reader.HasRows)
{
reader.Read();
pictureBox1.Image = ImageConverter.
ConvertByteArrayToImage((byte[])(reader.GetValue(0)));
}
else
{
pictureBox1.Image = null;
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Something is wrong with the selected record!
\nError: { ex.Message }");
}
}
//This is in my ImageConverter class:
public static Image ConvertByteArrayToImage(byte[] buffer) //Get image from database
{
using (MemoryStream ms = new MemoryStream(buffer))
{
return Image.FromStream(ms);
}
}
NOTE: I don't display my image's binary data in my datagridview.
Saving and updating the image (with the users records) works fine.
After saving an image to the database, it does not display properly. But when I load it using OpenFileDialog the image displays just fine.
Loading the image using OpenFileDialog:
When I click a datagridview row to view a user record this is what the pictureBox looks like:
Why is this split in some sort? I have not seen any similar problem/solution about this. Most of them is about "Loading image from the database to pictureBox". But I have already done that.
Try using the MemoryStream.Write method.
Change this:
//This is in my ImageConverter class:
public static Image ConvertByteArrayToImage(byte[] buffer) //Get image from database
{
using (MemoryStream ms = new MemoryStream(buffer))
{
return Image.FromStream(ms);
}
}
to this:
//This is in my ImageConverter class:
public static Image ConvertByteArrayToImage(byte[] buffer) //Get image from database
{
using (MemoryStream ms = new MemoryStream)
{
ms.Write(buffer.ToArray(), 0, buffer.Length);
return Image.FromStream(ms);
}
}
This is my approach for getting images from database
// This method use to update the form.
private void loadFormWithID(int ID)
{
dbServer conn = new dbServer(sysController.getConn);
DataTable tbl = conn.getQueryList("SELECT * FROM Products WHERE ID = " + ID);
DataRow row = tbl.Rows[0];
// This is how i update the Picture Box
pictureBoxItem.Image = row["Image"] == DBNull.Value ? pictureBoxItem.InitialImage : ImageController.bytesToImage((byte[])row["Image"]);
}
This is my dbserver class which communicates with database.
public class dbServer
{
public string _connectionLink;
public dbServer(string connectionString)
{
_connectionLink = connectionString;
}
public DataTable getQueryList(string sqlQuery)
{
DataTable tbl = new DataTable();
using (SqlConnection conn = new SqlConnection(_connectionLink))
{
using (SqlCommand cmd = new SqlCommand(sqlQuery, conn))
{
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
tbl.Load(reader);
}
}
return tbl;
}
}
I hope this solves the Issue.
This is what i used for my database image retriever.
class ImageController
{
public static byte[] ImageToBytes(PictureBox pb)
{
MemoryStream ms = new MemoryStream();
pb.Image.Save(ms, pb.Image.RawFormat);
return ms.GetBuffer();
}
public static byte[] ImageToBytes(Image pb)
{
MemoryStream ms = new MemoryStream();
pb.Save(ms, pb.RawFormat);
Console.WriteLine(ms.Length);
return ms.GetBuffer();
}
public static Image bytesToImage(byte[] imageRaw)
{
MemoryStream ms = new MemoryStream(imageRaw);
return Image.FromStream(ms);
}
}
Here's a complete solution, which seems to work with SQL Server Express/SQL Server:
Note: When the table in the database is created, column User_Image should be created as varbinary(MAX)
Read image from file and return as byte[]:
Note: I've included 3 different ways to read an image file and return a byte[]. GetImageFromFile seems to produce a byte array that has the same number of bytes as the original (tested with .jpg), whereas, GetImageFromFilev2 and GetImageFromFilev3, have fewer bytes. See How to convert image to byte array for more information.
public static byte[] GetImageFromFile(string filename)
{
byte[] rawData = null;
try
{
if (!String.IsNullOrEmpty(filename) && System.IO.File.Exists(filename))
{
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
//get length of file - in bytes
int fileLength = (int)fs.Length;
//create new byte array
rawData = new byte[fileLength];
//read data into byte array (rawData)
fs.Read(rawData, 0, fileLength);
fs.Flush();
Debug.WriteLine("rawData.Length: " + rawData.Length);
}
}
}
catch (Exception ex)
{
//ToDo: log message
throw ex;
}
return rawData;
}
public static byte[] GetImageFromFilev2(string filename)
{
byte[] rawData = null;
try
{
if (!String.IsNullOrEmpty(filename) && System.IO.File.Exists(filename))
{
using (Image image = Image.FromFile(filename))
{
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, image.RawFormat);
rawData = ms.ToArray();
}
}
}
}
catch (Exception ex)
{
//ToDo: log message
throw ex;
}
return rawData;
}
public static byte[] GetImageFromFilev3(string filename)
{
byte[] rawData = null;
try
{
if (!String.IsNullOrEmpty(filename) && System.IO.File.Exists(filename))
{
using (Image image = Image.FromFile(filename))
{
ImageConverter ic = new ImageConverter();
rawData = (byte[])ic.ConvertTo(image, typeof(byte[]));
Debug.WriteLine("rawData.Length: " + rawData.Length);
}
}
}
catch (Exception ex)
{
//ToDo: log message
throw ex;
}
return rawData;
}
Read image data from database:
public static System.Drawing.Bitmap GetImageFromTblEmployeeImageBitmap(string employee_id)
{
System.Drawing.Bitmap image = null;
byte[] imageData = GetImageFromTblEmployeeImageByte(employee_id);
//convert to Bitmap
if (imageData != null)
{
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(imageData))
{
image = new System.Drawing.Bitmap(ms);
ms.Flush();
}
}
return image;
}
public static byte[] GetImageFromTblEmployeeImageByte(string employee_id)
{
byte[] imageData = null;
try
{
using (SqlConnection cn = new SqlConnection(ConnectStr))
{
string sqlText = "Select user_image from Employee_Image where employee_id = #employee_id";
//open connection to db
cn.Open();
using (SqlCommand cmd = new SqlCommand(sqlText, cn))
{
cmd.Parameters.Add("#employee_id", SqlDbType.NVarChar).Value = employee_id;
//execute
SqlDataReader dr1 = cmd.ExecuteReader();
bool result = dr1.Read();
if (result)
{
imageData = (byte[])dr1["User_Image"];
}
Debug.WriteLine("result: " + result);
}
}
}
catch (SqlException ex)
{
//ToDo: log message
throw ex;
}
catch (Exception ex)
{
//ToDo: log message
throw ex;
}
return imageData;
}
Save image data to database
public static string SaveImageToTblEmployeeImage(string employee_id, string filename)
{
return SaveImageToTblEmployeeImage(employee_id, GetImageFromFile(filename));
}
public static string SaveImageToTblEmployeeImage(string employee_id, byte[] user_image)
{
string status = string.Empty;
using (SqlConnection cn = new SqlConnection(ConnectStr))
{
string sqlText = "INSERT INTO Employee_Image(Employee_Id, User_Image) VALUES (#employee_id, #user_image)";
//open connection to db
cn.Open();
using (SqlCommand cmd = new SqlCommand(sqlText, cn))
{
//add parameters
cmd.Parameters.Add("#employee_id", System.Data.SqlDbType.NVarChar).Value = employee_id;
//for varbinary(max) specify size = -1, otherwise there is an 8000 byte limit
//see https://learn.microsoft.com/en-us/dotnet/api/system.data.sqldbtype?view=netframework-4.8
cmd.Parameters.Add("#user_image", System.Data.SqlDbType.VarBinary, -1).Value = user_image;
//execute
int numRowsAffected = cmd.ExecuteNonQuery();
status = "Data inserted into table 'Employee_Image'";
Debug.WriteLine("numRowsAffected: " + numRowsAffected);
}
}
return status;
}
Upload image to database
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "Image Files (*.bmp;*.gif;*.jpg;*.jpeg;*.png)|*.bmp;*.gif;*.jpg;*.jpeg;*.png|All Files (*.*)|*.*";
if (ofd.ShowDialog() == DialogResult.OK)
{
SaveImageToTblEmployeeImage("12345", ofd.FileName);
}
}
To display image in PictureBox (ex: pictureBox1)
Bitmap image = GetImageFromTblEmployeeImageBitmap("12345");
if (image != null)
{
pictureBox1.Image = image;
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage; //fit to size
pictureBox1.Refresh();
}
Resources:
Getting binary data using SqlDataReader
How to save image in database using C#
SqlDbType Enum

Simple InputOutput method

I'm writing simple method that should read data from the text file.I can't understand why this method read only 2nd, 4th, 6 lines? Method is below. What is wrong in my code?
public static List<Employee> ReadFromFile(string path = "1.txt")
{
List<Employee> employees = new List<Employee>();
Stream stream = null;
StreamReader sr = null;
try
{
stream = new FileStream(path, FileMode.Open, FileAccess.Read);
stream.Seek(0, SeekOrigin.Begin);
sr = new StreamReader(stream);
string line;
while ((line = sr.ReadLine()) != null)
{
Employee employee = new DynamicEmployee();
string str = sr.ReadLine();
employee.FirstName = str.Substring(1, 20).Trim();
employee.LasttName = str.Substring(20, 20).Trim();
employee.Paynment = Convert.ToDouble(str.Substring(40, 20).Trim());
Console.WriteLine("{0} {1} {2}", employee.FirstName, employee.LasttName, employee.Paynment);
employees.Add(employee);
//Console.WriteLine(str);
}
}
catch//(System.FormatException)
{
Console.WriteLine("File format is incorect");
}
finally
{
sr.Close();
stream.Close();
}
return employees;
}
You are calling line = sr.ReadLine() twice.
Remove this line, string str = sr.ReadLine(); and use variable line
It should look like this:
public static List<Employee> ReadFromFile(string path = "1.txt")
{
List<Employee> employees = new List<Employee>();
Stream stream = null;
StreamReader sr = null;
try
{
stream = new FileStream(path, FileMode.Open, FileAccess.Read);
stream.Seek(0, SeekOrigin.Begin);
sr = new StreamReader(stream);
string line;
while ((line = sr.ReadLine()) != null)
{
Employee employee = new DynamicEmployee();
// string str = sr.ReadLine(); // WRONG, reading 2x
employee.FirstName = line.Substring(1, 20).Trim();
employee.LasttName = line.Substring(20, 20).Trim();
employee.Paynment = Convert.ToDouble(line.Substring(40, 20).Trim());
Console.WriteLine("{0} {1} {2}", employee.FirstName, employee.LasttName, employee.Paynment);
employees.Add(employee);
//Console.WriteLine(str);
}
}
catch//(System.FormatException)
{
Console.WriteLine("File format is incorect");
}
finally
{
sr.Close();
stream.Close();
}
return employees;
}

How to uploadfile using WCF 4.0 Template (REST)

How to upload file (500 MB size and up...) in Restful webservice then save the file in specific location?
If you have links, please share.
I use Fiddler to test the service.
Here are codes that I've been working for.
[WebInvoke(UriTemplate = "Add", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
public bool UploadFile(FileUploader.File userFile, System.IO.Stream fileStream)
{
FileUploader.Logic.StreamObject streamUploader = new FileUploader.Logic.StreamObject();
streamUploader.UploadFile(userFile.FileName, fileStream);
return true;
}
public class StreamObject : IStreamObject
{
public void UploadFile(string filename, Stream fileStream)
{
byte[] buffer = new byte[10000];
int bytesRead, totalbytesRead = 0;
do
{
bytesRead = fileStream.Read(buffer, 0, buffer.Length);
totalbytesRead += bytesRead;
} while (bytesRead > 0);
}
}
An example on how to upload a file to a REST service is shown below:
private byte[] UseWebClientForFileUpload(string serviceBaseUrl, String resourceUrl, string filePath)
{
var c = new WebClient();
c.OpenWrite(string.Concat(serviceBaseUrl, resourceUrl), "POST");
c.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
return c.UploadFile(string.Concat(serviceBaseUrl, resourceUrl), filePath);
}
Make sure to set appropriate content type. I think we cannot pass multiple parameters when using Stream as one of the params, so in order to get the filename and the stream just pass everything as a single stream and then use a parser that would seperate your stream. There is something called a multipartParser as shown below:
public class MultipartParser
{
public MultipartParser(Stream stream)
{
this.Parse(stream, Encoding.UTF8);
ParseParameter(stream, Encoding.UTF8);
}
public MultipartParser(Stream stream, Encoding encoding)
{
this.Parse(stream, encoding);
}
private void Parse(Stream stream, Encoding encoding)
{
this.Success = false;
// Read the stream into a byte array
byte[] data = ToByteArray(stream);
// Copy to a string for header parsing
string content = encoding.GetString(data);
// The first line should contain the delimiter
int delimiterEndIndex = content.IndexOf("\r\n");
if (delimiterEndIndex > -1)
{
string delimiter = content.Substring(0, content.IndexOf("\r\n"));
// Look for Content-Type
Regex re = new Regex(#"(?<=Content\-Type:)(.*?)(?=\r\n\r\n)");
Match contentTypeMatch = re.Match(content);
// Look for filename
re = new Regex(#"(?<=filename\=\"")(.*?)(?=\"")");
Match filenameMatch = re.Match(content);
// Did we find the required values?
if (contentTypeMatch.Success && filenameMatch.Success)
{
// Set properties
this.ContentType = contentTypeMatch.Value.Trim();
this.Filename = filenameMatch.Value.Trim();
// Get the start & end indexes of the file contents
int startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length;
byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter);
int endIndex = IndexOf(data, delimiterBytes, startIndex);
int contentLength = endIndex - startIndex;
// Extract the file contents from the byte array
byte[] fileData = new byte[contentLength];
Buffer.BlockCopy(data, startIndex, fileData, 0, contentLength);
this.FileContents = fileData;
this.Success = true;
}
}
}
private void ParseParameter(Stream stream, Encoding encoding)
{
this.Success = false;
// Read the stream into a byte array
byte[] data = ToByteArray(stream);
// Copy to a string for header parsing
string content = encoding.GetString(data);
// The first line should contain the delimiter
int delimiterEndIndex = content.IndexOf("\r\n");
if (delimiterEndIndex > -1)
{
string delimiter = content.Substring(0, content.IndexOf("\r\n"));
string[] splitContents = content.Split(new[] {delimiter}, StringSplitOptions.RemoveEmptyEntries);
foreach (string t in splitContents)
{
// Look for Content-Type
Regex contentTypeRegex = new Regex(#"(?<=Content\-Type:)(.*?)(?=\r\n\r\n)");
Match contentTypeMatch = contentTypeRegex.Match(t);
// Look for name of parameter
Regex re = new Regex(#"(?<=name\=\"")(.*)");
Match name = re.Match(t);
// Look for filename
re = new Regex(#"(?<=filename\=\"")(.*?)(?=\"")");
Match filenameMatch = re.Match(t);
// Did we find the required values?
if (name.Success || filenameMatch.Success)
{
// Set properties
//this.ContentType = name.Value.Trim();
int startIndex;
if (filenameMatch.Success)
{
this.Filename = filenameMatch.Value.Trim();
}
if(contentTypeMatch.Success)
{
// Get the start & end indexes of the file contents
startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length;
}
else
{
startIndex = name.Index + name.Length + "\r\n\r\n".Length;
}
//byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter);
//int endIndex = IndexOf(data, delimiterBytes, startIndex);
//int contentLength = t.Length - startIndex;
string propertyData = t.Substring(startIndex - 1, t.Length - startIndex);
// Extract the file contents from the byte array
//byte[] paramData = new byte[contentLength];
//Buffer.BlockCopy(data, startIndex, paramData, 0, contentLength);
MyContent myContent = new MyContent();
myContent.Data = encoding.GetBytes(propertyData);
myContent.StringData = propertyData;
myContent.PropertyName = name.Value.Trim();
if (MyContents == null)
MyContents = new List<MyContent>();
MyContents.Add(myContent);
this.Success = true;
}
}
}
}
private int IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex)
{
int index = 0;
int startPos = Array.IndexOf(searchWithin, serachFor[0], startIndex);
if (startPos != -1)
{
while ((startPos + index) < searchWithin.Length)
{
if (searchWithin[startPos + index] == serachFor[index])
{
index++;
if (index == serachFor.Length)
{
return startPos;
}
}
else
{
startPos = Array.IndexOf<byte>(searchWithin, serachFor[0], startPos + index);
if (startPos == -1)
{
return -1;
}
index = 0;
}
}
}
return -1;
}
private byte[] ToByteArray(Stream stream)
{
byte[] buffer = new byte[32768];
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}
public List<MyContent> MyContents { get; set; }
public bool Success
{
get;
private set;
}
public string ContentType
{
get;
private set;
}
public string Filename
{
get;
private set;
}
public byte[] FileContents
{
get;
private set;
}
}
public class MyContent
{
public byte[] Data { get; set; }
public string PropertyName { get; set; }
public string StringData { get; set; }
}
I did get the multipartParser from a here
The answer stems from what I have done before. The service may look as follows:
[ServiceContract]
public class DocumentService
{
[OperationContract]
[WebTemplate("{name}"]
public Document Create(Stream stream, string name)
{
var id = Guid.NewGuid().ToString("N");
using(FileStream outputStream = File.Create(Path.Combine("c:\\temp\\", id)))
{
stream.CopyTo(outputStream);
}
Document document = new Document();
document.Name = name;
document.Id = id;
// Save document to database
// Set headers
return document;
}
}
where Document may look as follows:
[DataContract]
public class Document
{
[DataMember]
public string Id
{
get;set;
}
[DataMember]
public string Name
{
get;set;
}
/* other fields */
}
To upload a file to the service (assuming it is at http://api.example.com/documents/), you can do the following:
string name = "mydocument.doc";
var request = WebRequest.Create ("http://api.example.com/documents/" + name);
request.ContentType = "application/octet-stream";
request.Method = "POST";
using(var stream = request.GetRequestStream())
{
using(var inputStream = File.OpenRead("c:\\mydocument.doc"))
{
inputStream.CopyTo(stream);
}
}
using(var response = (HttpWebResponse)request.GetResponse())
{
// process the response, if needed
}
There is no need to send a multipart stream over if all you do is sending one stream. It is important that any parameters (such as name) should be part of the UriTemplate.

How to keep XmlSerializer from killing NewLines in Strings?

Suppose I have a simple Class with just one Member a String.
public class Abc
{
private String text;
public String Text
{
get { return this.text; }
set { this.text = value; }
}
}
Now when I serialize and then deserialize it with the questionable XmlSerializer any text containing newlines ('\r\n' or Environment.NewLine) are transformed to '\n'.
How do I keep the newlines?
It is not the XmlSerializer but the XmlWriter which is removing your CR. To retain it we must have the writer convert CR to its character entity 
.
XmlWriterSettings ws = new XmlWriterSettings();
ws.NewLineHandling = NewLineHandling.Entitize;
XmlSerializer ser = new XmlSerializer( typeof( Abc ) );
using (XmlWriter wr = XmlWriter.Create( "abc.xml", ws )) {
ser.Serialize( wr, s );
}
This is exactly the same with DataContractSerializer:
var ser = new DataContractSerializer( typeof( Abc ) );
using (XmlWriter wr = XmlWriter.Create( "abc.xml", ws )) {
ser.Serialize( wr, s );
}
Why do we need to do this?
This is because compliant XML parsers must, before parsing, translate CRLF and any CR not followed by a LF to a single LF. This behavior is defined in the End-of-Line handling section of the XML 1.0 specification.
As this happens before parsing, you need to encode CR as its character entity if you want the CR to exist in the document.
public class SerializeAny<TF> where TF : new()
{
public static TF Deserialize(string serializedData)
{
try
{
var xmlSerializer = new XmlSerializer(typeof(TF));
TF collection;
using (var xmlReader = new XmlTextReader(serializedData, XmlNodeType.Document, null))
{
collection = (TF)xmlSerializer.Deserialize(xmlReader);
}
return collection;
}
catch (Exception)
{
}
return new TF();
}
public static TF DeserializeZip(string path)
{
try
{
var bytes = File.ReadAllBytes(path);
string serializedData = Unzip(bytes);
TF collection = Deserialize(serializedData);
return collection;
}
catch (Exception)
{
}
return new TF();
}
public static string Serialize(TF options)
{
var xml = "";
try
{
var xmlSerializer = new XmlSerializer(typeof(TF));
using (var stringWriter = new StringWriter())
{
xmlSerializer.Serialize(stringWriter, options);
xml = stringWriter.ToString();
}
}
catch (Exception ex)
{
return ex.Message;
}
return xml;
}
public static string SerializeZip(TF options, string path)
{
var xml = "";
try
{
xml = Serialize(options);
var zip = Zip(xml);
File.WriteAllBytes(path, zip);
}
catch (Exception ex)
{
return ex.Message;
}
return xml;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
internal static String SerializeObject<T>(T obj, Encoding enc)
{
using (var ms = new MemoryStream())
{
var xmlWriterSettings = new System.Xml.XmlWriterSettings()
{
// If set to true XmlWriter would close MemoryStream automatically and using would then do double dispose
// Code analysis does not understand that. That's why there is a suppress message.
CloseOutput = false,
Encoding = enc,
OmitXmlDeclaration = false,
Indent = true
};
using (var xw = XmlWriter.Create(ms, xmlWriterSettings))
{
var s = new XmlSerializer(typeof(T));
s.Serialize(xw, obj);
}
return enc.GetString(ms.ToArray());
}
}
private static void CopyTo(Stream src, Stream dest)
{
byte[] bytes = new byte[4096];
int cnt;
while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0)
{
dest.Write(bytes, 0, cnt);
}
}
private static byte[] Zip(string str)
{
var bytes = Encoding.UTF8.GetBytes(str);
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gs = new GZipStream(mso, CompressionMode.Compress))
{
//msi.CopyTo(gs);
CopyTo(msi, gs);
}
return mso.ToArray();
}
}
private static string Unzip(byte[] bytes)
{
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gs = new GZipStream(msi, CompressionMode.Decompress))
{
CopyTo(gs, mso);
}
return Encoding.UTF8.GetString(mso.ToArray());
}
}
}
public class BinarySerialize<T> where T : new()
{
public static string Serialize(T options, string path)
{
var xml = "";
try
{
File.Delete(path);
}
catch (Exception)
{
}
try
{
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
var bf = new BinaryFormatter();
bf.Serialize(fs, options);
}
}
catch (Exception ex)
{
return ex.Message;
}
return xml;
}
public static T Deserialize(string path)
{
T filteroptions;
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var bf = new BinaryFormatter();
filteroptions = (T)bf.Deserialize(fs);
}
return filteroptions;
}
}
Use this code:
public static FilterOptions Deserialize(string serializedData)
{
try
{
var xmlSerializer = new XmlSerializer(typeof(FilterOptions));
var xmlReader = new XmlTextReader(serializedData,XmlNodeType.Document,null);
var collection = (FilterOptions)xmlSerializer.Deserialize(xmlReader);
return collection;
}
catch (Exception)
{
}
return new FilterOptions();
}
Nice solution, Lachlan Roche!
The function below (in VB.NET) uses a StringWriter to return a String, rather than writing the result to a file using an XmlWriter.
''' <summary>
''' Exports the object data to an XML formatted string.
''' Maintains CR characters after deserialization.
''' The object must be serializable to work.
''' </summary>
Public Function ExportObjectXml(ByVal obj As Object) As String
If obj Is Nothing Then
Return String.Empty
End If
Dim serializer As New XmlSerializer(obj.GetType)
Dim settings As New XmlWriterSettings With {.NewLineHandling = NewLineHandling.Entitize}
Using output As New StringWriter
Using writer As XmlWriter = XmlWriter.Create(output, settings)
serializer.Serialize(writer, obj)
Return output.ToString
End Using
End Using
End Function

Categories

Resources