I have a C# app.
It uploads images to my server and downloads them when necessary.
This is my client:
sb.Clear();
sb.Append(GeneralTags.ACTION_ADD);
sb.Append(Shared.DLE);
sb.Append("GALLERY");
sb.Append(Shared.DLE);
sb.Append(Shared.CurrentClientId);
sb.Append(Shared.DLE);
sb.Append(JsonConvert.SerializeObject(gallery));
sb.Append(Shared.DLE);
byte[] byteArray = System.Web.HttpUtility.UrlEncodeToBytes(sb.ToString());
int packetCount = 0;
using (TcpClient clientSocket = new TcpClient())
{
clientSocket.Connect(GeneralTags.RASPBERRY_PI_IP_ADDRESS, GeneralTags.RASPBERRY_PI_PORT);
using (NetworkStream serverStream = clientSocket.GetStream())
{
serverStream.Write(byteArray, 0, byteArray.Length);
serverStream.Flush();
List<byte> requestInBytes = new List<byte>();
if (serverStream.CanRead)
{
int i = 0;
Byte[] bytesIn = new Byte[1024];
do
{
i = serverStream.Read(bytesIn, 0, bytesIn.Length);
byte[] receivedBuffer = new byte[i];
Array.Copy(bytesIn, receivedBuffer, i);
if (packetCount == 0)
{
packetCount = BitConverter.ToInt32(receivedBuffer, 0);
}
requestInBytes.AddRange(receivedBuffer);
} while (packetCount < requestInBytes.Count);
serverStream.Close();
var json = Encoding.UTF8.GetString(requestInBytes.ToArray(), 0, requestInBytes.ToArray().Length);
var galleryback = JsonConvert.DeserializeObject<Gallery>(json);
using (MemoryStream ms = new MemoryStream())
{
System.Diagnostics.Debug.WriteLine("BACK: " + galleryback.ImageData.Length.ToString());
ms.Write(galleryback.ImageData, 0, galleryback.ImageData.Length);
Shared.ViewImage(Image.FromStream(ms, true));
}
}
else
{
MessageBox.Show("Local Device Error");
}
}
}
This is my class object:
public class Gallery
{
public string Title { get; set; }
public string Description { get; set; }
public byte[] ImageData { get; set; }
public bool IsUploaded { get; set; }
public Int64 GalleryId { get; set; }
public string JobRef { get; set; }
}
If we assume that my server is sending the entire data array back correctly (I have checked the bytes sent from server with the bytes received by my client)...
I get these type of errors:
Unterminated string. Expected delimiter: ". Path 'ImageData', line 1, position 5515.
Unexpected character encountered while parsing value: �. Path '', line 0, position 0.
How can I solve this?
thanks
It looks like you are sending a byte array with the serialized JSON as only a part of it (you append other things to the string, like GeneralTags.ACTION_ADD and so on), but when you receive data you try to deserialize the whole thing. Of course, since you do not have data in JSON format only, this results in DeserializeObject not being able to parse the data passed to it. You need to extract the serialized object in some way, for example using regex or some calculation involving substrings.
Related
I have googled my *** off and cannot find a reason why this is happening.
I have a WCF service (file) where I upload a PDF that is only in a memorystream. The memorystream shows I have data before it is sent to the service. When it gets to the service the stream is empty. I have a MessageContract for the upload that has a MessageBodyMember of Stream. I call the upload in the service and pass the parameters (the same ones that are in the MessageContract. When I look at the upload request object I can see all the values except Stream.
Here is my UploadRequest class:
[MessageContract]
public class UploadRequest
{
[MessageHeader(MustUnderstand = true)]
public long EntityID { get; set; }
[MessageHeader(MustUnderstand = true)]
public string FileName { get; set; }
[MessageHeader(MustUnderstand = false)]
public bool IsPrivileged { get; set; }
[MessageHeader(MustUnderstand = false)]
public string Folder { get; set; }
[MessageHeader(MustUnderstand = false)]
public Guid stream_id { get; set; }
[MessageBodyMember(Order = 1)]
public Stream Stream { get; set; }
}
Here is my call to the upload method in the service:
private void SaveAs()
{
var UserID = Convert.ToInt64(Application.Current.Properties["VetID"]);
var ms = new MemoryStream();
Control.SaveDocument(ms);
ms.Position = 0;
if (!svc.Upload(UserID, null, null, true, gVar.stream_id, ms, out string errormsg))
_ = errormsg;
}
Note: I did set the MemoryStream position to 0 and the object ms has data.
Here is FileService upload method:
public UploadResponse Upload(UploadRequest request)
{
DB.dbFileMgr fm = new DB.dbFileMgr();
return fm.UploadFile(request);
}
And here is the UploadFile method:
if (request.stream_id != null && request.stream_id != new Guid())
{
var stream = SharedCommon.ConvertStreamToMemoryStream(request.Stream);
dbConn.UpdateDocument(request.EntityID, request.IsPrivileged, request.stream_id, stream);
return new UploadResponse { UploadSucceeded = true };
}
else
{
dbConn.CreateDocumentFolder(request.Folder, request.EntityID);
var stream = SharedCommon.ConvertStreamToMemoryStream(request.Stream);
dbConn.InsertDocument(request.EntityID, stream, request.FileName, request.Folder, request.IsPrivileged);
return new UploadResponse { UploadSucceeded = true };
}
Here is what the Stream is when I put a breakpoint at the Upload:
Debug info of the Stream
I have a method to convert a Stream to a MemoryStream, thinking that would help, but when it reaches the while loop in the ReadFully method it skips over it. That tells me the Stream is empty.
Here is the code for the Convert and ReadFully methods:
public static MemoryStream ConvertStreamToMemoryStream(Stream stream)
{
MemoryStream memoryStream = new MemoryStream();
if (stream != null)
{
byte[] buffer = ReadFully(stream);
if (buffer != null)
{
var binaryWriter = new BinaryWriter(memoryStream);
binaryWriter.Write(buffer);
}
}
return memoryStream;
}
public static byte[] ReadFully(this Stream input)
{
byte[] buffer = new byte[48 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
I have read all the Microsoft, C-SharpCorner & StackOverflow articles and I am at a loss as to what is wrong. I would appreciate any ideas as to what is going on and how to fix this.
Edit
Maybe I did not make this clear, I am sorry. I can upload a file from the file system using the FileUpload.InputStream but when I try and send a dynamically created PDF that is only in a MemoryStream to the same service as the File Upload it fails. Before anyone says that the file might be bad, I can save the MemoryStream to my desktop and the file opens fine in Adobe. The issue seems to be sending a MemoryStream through MessageContract. It seems to have no problem with an InputStream (FileUpload) but not a MemoryStream.
Stream.Length only works on Stream implementations where seeking is available. You can usually check to see if Stream.CanSeek is true. Many streams, since they're being streamed, are of a nature where it's impossible to know the length in advance.
If you must know the length, you may need to actually buffer the entire stream, loading it into memory in advance.
You can consider copying the uploaded file to MemoryStream:
public void Upload(UploadRequest request)
{
MemoryStream outstream = new MemoryStream();
const int bufferLen = 4096;
byte[] buffer = new byte[bufferLen];
int count = 0;
while ((count = request.Stream.Read(buffer, 0, bufferLen)) > 0)
{
outstream.Write(buffer, 0, count);
}
byte[] b = outstream.ToArray();
string s = System.Text.Encoding.UTF8.GetString(b, 0, b.Length);
Console.WriteLine(s);
}
UPDATE:
You need to modify your ConvertStreamToMemoryStream method:
public static MemoryStream ConvertStreamToMemoryStream(Stream stream)
{
MemoryStream outstream = new MemoryStream();
const int bufferLen = 48 * 1024;
byte[] buffer = new byte[bufferLen];
int count = 0;
while ((count = stream.Read(buffer, 0, bufferLen)) > 0)
{
outstream.Write(buffer, 0, count);
}
return outstream;
}
I need to attach a PDF document and send it to the WCF server which will extract it some specific details and sends a response back to the client. Please suggest me how to acheive this in C# .Net
well you can encode your file as base 64 using following method at client end:
private string EncodeFileAsBase64(string inputFileName)
{
byte[] binaryData;
using (System.IO.FileStream inFile = new System.IO.FileStream(inputFileName,
System.IO.FileMode.Open,
System.IO.FileAccess.Read))
{
binaryData = new Byte[inFile.Length];
long bytesRead = inFile.Read(binaryData, 0,
(int)inFile.Length);
inFile.Close();
}
// Convert the binary input into Base64 UUEncoded output.
string base64String;
base64String = System.Convert.ToBase64String(binaryData, 0, binaryData.Length);
return base64String;
}
Now at service end you can have the physical file and you can perform whatever the operation you want. This will ensure the security aspects of the file while transmitting it over network.
This will allow you to upload a file to a WCF service:
Create a service contract in your interface for your Upload service:
[ServiceContract]
public interface IVO_Upload
{
[OperationContract]
string UploadFile(RemoteFileInfo request);
}
[MessageContract]
public class RemoteFileInfo : IDisposable
{
[MessageHeader(MustUnderstand = true)]
public string FileName;
[MessageHeader(MustUnderstand = true)]
public long Length;
[MessageHeader(MustUnderstand = true)]
public string Response;
[MessageBodyMember(Order = 1)]
public System.IO.Stream FileByteStream;
public void Dispose()
{
if (FileByteStream != null)
{
FileByteStream.Close();
FileByteStream = null;
}
}
}
Then implement this interface in your .svc class:
public class VO_eLoyalty : IVO_Upload
{
string IVO_Upload.UploadFile(RemoteFileInfo request)
{
string _uploadFolder = "PATH_TO_SAVE_FILE";
string filePath = Path.Combine(_uploadFolder, request.FileName);
using (targetStream = new FileStream(filePath, FileMode.Create,
FileAccess.Write, FileShare.None))
{
//read from the input stream in 65000 byte chunks
const int bufferLen = 65000;
byte[] buffer = new byte[bufferLen];
int count = 0;
while ((count = sourceStream.Read(buffer, 0, bufferLen)) > 0)
{
// save to output stream
targetStream.Write(buffer, 0, count);
}
targetStream.Close();
sourceStream.Close();
}
return "details"; // return whatever you need to here, or change the return type to whatever you need
}
}
I presume you are happy with extracting specific details from the PDF file and returning the response?
I'm making a simple webserver to serve html, css, js & images (done in c#). I am using HttpListener and I can get the html, javascript and css files to work properly. I am just having trouble with the images. This is what I'm using currently:
if (request.RawUrl.ToLower().Contains(".png") || request.RawUrl.Contains(".ico") || request.RawUrl.ToLower().Contains(".jpg") || request.RawUrl.ToLower().Contains(".jpeg"))
{
string dir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string[] img = request.RawUrl.Split('/');
string path = dir + #"\public\imgs\" + img[img.Length - 1];
FileInfo fileInfo = new FileInfo(path);
long numBytes = fileInfo.Length;
FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
BinaryReader binaryReader = new BinaryReader(fileStream);
byte[] output = binaryReader.ReadBytes((int)numBytes);
binaryReader.Close();
fileStream.Close();
var temp = System.Text.Encoding.UTF8.GetString(output);
return temp;
}
I am converting the image into a string to return them (it's the way my boss suggested). This is the method where I am handling these requests.
private static string SendResponse(HttpListenerRequest request)
This is my WebServer classes Run() method. The call to SetContentType just goes through the request.RawUrl and determines the content type.
public void Run()
{
ThreadPool.QueueUserWorkItem((o) =>
{
Console.WriteLine("StackLight Web Server is running...");
try
{
while (_listener.IsListening)
{
ThreadPool.QueueUserWorkItem((c) =>
{
var ctx = c as HttpListenerContext;
try
{
// store html content in a byte array
string responderString = _responderMethod(ctx.Request);
// set the content type
ctx.Response.Headers[HttpResponseHeader.ContentType] = SetContentType(ctx.Request.RawUrl);
byte[] buffer = buffer = Encoding.UTF8.GetBytes(responderString);
// this writes the html out from the byte array
ctx.Response.ContentLength64 = buffer.Length;
using(Stream stream = ctx.Response.OutputStream)
{
stream.Write(buffer, 0, buffer.Length);
}
}
catch (Exception ex)
{
ConfigLogger.Instance.LogCritical(LogCategory, ex);
}
}, _listener.GetContext());
}
}
catch (Exception ex)
{
ConfigLogger.Instance.LogCritical(LogCategory, ex);
}
});
}
My html page needs to display an image to the screen, it displays a broken image so far. I know the images directory is correct, I tested that.
This is where I got my code for the webserver: here
I was thinking that maybe I have to change the SendResponse method to not return a string
I figured it out. I created a class to hold the data, content type and the request.RawUrl. Then, where I was passing a string, I changed it to pass the object I created.
So, for my WebServer class, my Run method looks like this:
public void Run()
{
ThreadPool.QueueUserWorkItem((o) =>
{
Console.WriteLine("StackLight Web Server is running...");
try
{
while (_listener.IsListening)
{
ThreadPool.QueueUserWorkItem((c) =>
{
var ctx = c as HttpListenerContext;
try
{
// set the content type
ctx.Response.Headers[HttpResponseHeader.ContentType] = SetContentType(ctx.Request.RawUrl);
WebServerRequestData data = new WebServerRequestData();
// store html content in a byte array
data = _responderMethod(ctx.Request);
string res = "";
if(data.ContentType.Contains("text"))
{
char[] chars = new char[data.Content.Length/sizeof(char)];
System.Buffer.BlockCopy(data.Content, 0, chars, 0, data.Content.Length);
res = new string(chars);
data.Content = Encoding.UTF8.GetBytes(res);
}
// this writes the html out from the byte array
ctx.Response.ContentLength64 = data.Content.Length;
ctx.Response.OutputStream.Write(data.Content, 0, data.Content.Length);
}
catch (Exception ex)
{
ConfigLogger.Instance.LogCritical(LogCategory, ex);
}
finally
{
ctx.Response.OutputStream.Close();
}
}, _listener.GetContext());
}
}
catch (Exception ex)
{
ConfigLogger.Instance.LogCritical(LogCategory, ex);
}
});
}
And my SendResponse method looks like this:
private static WebServerRequestData SendResponse(HttpListenerRequest request)
{
string dir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string[] fileUrl = request.RawUrl.Split('/');
// routes
if (request.RawUrl.Contains("/"))
{
// this is the main page ('/'), all other routes can be accessed from here (including css, js, & images)
if (request.RawUrl.ToLower().Contains(".png") || request.RawUrl.ToLower().Contains(".ico") || request.RawUrl.ToLower().Contains(".jpg") || request.RawUrl.ToLower().Contains(".jpeg"))
{
try
{
string path = dir + Properties.Settings.Default.ImagesPath + fileUrl[fileUrl.Length - 1];
FileInfo fileInfo = new FileInfo(path);
path = dir + #"\public\imgs\" + fileInfo.Name;
byte[] output = File.ReadAllBytes(path);
_data = new WebServerRequestData() {Content = output, ContentType = "image/png", RawUrl = request.RawUrl};
//var temp = System.Text.Encoding.UTF8.GetString(output);
//return Convert.ToBase64String(output);
return _data;
}
catch(Exception ex)
{
ConfigLogger.Instance.LogError(LogCategory, "File could not be read.");
ConfigLogger.Instance.LogCritical(LogCategory, ex);
_errorString = string.Format("<html><head><title>Test</title></head><body>There was an error processing your request:<br />{0}</body></html>", ex.Message);
_byteData = new byte[_errorString.Length * sizeof(char)];
System.Buffer.BlockCopy(_errorString.ToCharArray(), 0, _byteData, 0, _byteData.Length);
_data = new WebServerRequestData() { Content = _byteData, ContentType = "text/html", RawUrl = request.RawUrl };
return _data;
}
}
I'm still cleaning up the code a bit but it now serves the images!
Oh... And here is the object I'm using:
public class WebServerRequestData
{
public string RawUrl { get; set; }
public string ContentType { get; set; }
public byte[] Content { get; set; }
public string RawData { get; set; }
}
Some really bad stuff here:
Empty catch. You'll never find out about many bugs.
Stuffing binary data into a string. Why? There's no encoding that is able to roundtrip binary data.
You're not disposing of ctx. I don't see why you need a manual finally block. Use using.
Untrusted callers can inject arbitrary paths into path. I could request your web.config file by navigating to /img/..\..\web.config (something like that).
Consider factoring out some common expressions into variables. You've got a Copy&Paste error with ToLower. Don't do dirty stuff and you'll have less bugs.
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.
Is there a way to upload a file, save it to a Stream, this Stream I will save it temporarily in a Session and, at last, I will try to preview this uploaded file that is in this Session??
For example, a pdf file.
Thanks!!
EDITED
Here's what I'm trying to do:
HttpPostedFileBase hpf = Request.Files[0] as HttpPostedFileBase;
byte[] buffer = new byte[hpf.InputStream.Length];
MemoryStream ms = new MemoryStream(buffer);
ms.Read(buffer, 0, (int)ms.Length);
Session["pdf"] = ms.ToArray();
ms.Close();
And in another method, I'm doing this:
byte[] imageByte = null;
imageByte = (byte[])Session["pdf"];
Response.ContentType = "application/pdf";
Response.Buffer = true;
Response.Clear();
Response.BinaryWrite(imageByte);
But nothing happends... my browser even opens a nem page to show the pdf file, but a window is shown saying that the file is not a pdf (or something like the file doesn't initiate with pdf, I didn't understand that)
Sure is. I upload files (PDF/images) to my db in my app. My model object actually stores the file as a byte array but for other functions i have to convert to and from streams so im sure its just as easy to keep it in stream format.
Here are some code examples (copy n paste) from my app-
The File object that i use to move files (PDFs / images) around:
public class File : CustomValidation, IModelBusinessObject
{
public int ID { get; set; }
public string MimeType { get; set; }
public byte[] Data { get; set; }
public int Length { get; set; }
public string MD5Hash { get; set; }
public string UploadFileName { get; set; }
}
..the PdfDoc type specifically for PDF files:
public class PdfDoc : File
{
public int ID { get; set; }
public int FileID
{
get { return base.ID; }
set { base.ID = value; }
}
[StringLength(200, ErrorMessage = "The Link Text must not be longer than 200 characters")]
public string LinkText { get; set; }
public PdfDoc() { }
public PdfDoc(File file)
{
MimeType = file.MimeType;
Data = file.Data;
Length = file.Length;
MD5Hash = file.MD5Hash;
UploadFileName = file.UploadFileName;
}
public PdfDoc(File file, string linkText)
{
MimeType = file.MimeType;
Data = file.Data;
Length = file.Length;
MD5Hash = file.MD5Hash;
UploadFileName = file.UploadFileName;
LinkText = linkText;
}
}
.. an example of an action that receives multi-part POST for file uploading:
//
// POST: /Announcements/UploadPdfToAnnouncement/ID
[KsisAuthorize(Roles = "Admin, Announcements")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UploadPdfToAnnouncement(int ID)
{
FileManagerController.FileUploadResultDTO files =
FileManagerController.GetFilesFromRequest((HttpContextWrapper)HttpContext);
if (String.IsNullOrEmpty(files.ErrorMessage) && files.TotalBytes > 0)
{
// add SINGLE file to the announcement
try
{
this._svc.AddAnnouncementPdfDoc(
this._svc.GetAnnouncementByID(ID),
new PdfDoc(files.Files[0]),
new User() { UserName = User.Identity.Name });
}
catch (ServiceExceptions.KsisServiceException ex)
{
// only handle our exceptions
base.AddErrorMessageLine(ex.Message);
}
}
// redirect back to detail page
return RedirectToAction("Detail", "Announcements", new { id = ID });
}
Now you can see i pass the file object to my service here but you can choose to add it to the session and pass an id back to the 'preview' view for example.
Finally, here is a generic action i use to render files out to the client (you could have something similar render the files/stream from the Session):
//
// GET: /FileManager/GetFile/ID
[OutputCache(Order = 2, Duration = 600, VaryByParam = "ID")]
public ActionResult GetFile(int ID)
{
FileService svc = ObjectFactory.GetInstance<FileService>();
KsisOnline.Data.File result = svc.GetFileByID(ID);
return File(result.Data, result.MimeType, result.UploadFileName);
}
EDIT:
I noticed i need more samples to explain the above-
For the upload action above, the FileUploadResultDTO class:
public class FileUploadResultDTO
{
public List<File> Files { get; set; }
public Int32 TotalBytes { get; set; }
public string ErrorMessage { get; set; }
}
And the GetFilesFromRequest function:
public static FileUploadResultDTO GetFilesFromRequest(HttpContextWrapper contextWrapper)
{
FileUploadResultDTO result = new FileUploadResultDTO();
result.Files = new List<File>();
foreach (string file in contextWrapper.Request.Files)
{
HttpPostedFileBase hpf = contextWrapper.Request.Files[file] as HttpPostedFileBase;
if (hpf.ContentLength > 0)
{
File tempFile = new File()
{
UploadFileName = Regex.Match(hpf.FileName, #"(/|\\)?(?<fileName>[^(/|\\)]+)$").Groups["fileName"].ToString(), // to trim off whole path from browsers like IE
MimeType = hpf.ContentType,
Data = FileService.ReadFully(hpf.InputStream, 0),
Length = (int)hpf.InputStream.Length
};
result.Files.Add(tempFile);
result.TotalBytes += tempFile.Length;
}
}
return result;
}
And finally (i hope i have everything you need now) this ReadFully function. It's not my design. I got it from the net - stream reading can be tricky. I find this function is the most successful way to completely read a stream:
/// <summary>
/// Reads data from a stream until the end is reached. The
/// data is returned as a byte array. An IOException is
/// thrown if any of the underlying IO calls fail.
/// </summary>
/// <param name="stream">The stream to read data from</param>
/// <param name="initialLength">The initial buffer length</param>
public static byte[] ReadFully(System.IO.Stream stream, long initialLength)
{
// reset pointer just in case
stream.Seek(0, System.IO.SeekOrigin.Begin);
// If we've been passed an unhelpful initial length, just
// use 32K.
if (initialLength < 1)
{
initialLength = 32768;
}
byte[] buffer = new byte[initialLength];
int read = 0;
int chunk;
while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0)
{
read += chunk;
// If we've reached the end of our buffer, check to see if there's
// any more information
if (read == buffer.Length)
{
int nextByte = stream.ReadByte();
// End of stream? If so, we're done
if (nextByte == -1)
{
return buffer;
}
// Nope. Resize the buffer, put in the byte we've just
// read, and continue
byte[] newBuffer = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuffer, buffer.Length);
newBuffer[read] = (byte)nextByte;
buffer = newBuffer;
read++;
}
}
// Buffer is now too big. Shrink it.
byte[] ret = new byte[read];
Array.Copy(buffer, ret, read);
return ret;
}
Yes, but you can't save it to a stream. A stream doesn't contain any data, it's just the means for accessing the actual storage.
Get the data as a byte array, then you can put it in a session variable, save it as a file, and send it as a response.
Use a BinaryReader to get the data from the input stream to a byte array:
byte[] data;
using (BinaryReader reader = new BinaryReader(uploadedFile.InputStream)) {
data = reader.ReadBytes((int) uploadedFile.InputStream.Length);
}
(Edit: Changed from a StreamReader to a BinaryReader)
byte[] data;
using (Stream inputStream = PdfPath.InputStream)
{
MemoryStream memoryStream = inputStream as MemoryStream;
if (memoryStream == null)
{
memoryStream = new MemoryStream();
inputStream.CopyTo(memoryStream);
}
data = memoryStream.ToArray();
}