When the file is sent to c# server through firefox, it works but it doesn't work on chrome.
In firefox, every files size can be received well.
in chrome, only small files are received. however, received bytes of Files larger than 50kb are not equal to the file size.
public class Client
{
private static readonly string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
public Socket Socket { set; get; }
private byte[] Buffer { get; set; }
private int BufferSize { get; set; } = 1024 * 1024;
private List<byte> Buffers = new List<byte>();
private BytesDecoder decoder = new BytesDecoder();
public void Listen()
{
SendHandshake();
BeginReceive();
}
private void SendHandshake()
{
try
{
byte[] buffer = new byte[1024];
Socket.Receive(buffer);
var key = new Regex(#"Sec-WebSocket-Key:\s(.*?)\r\n").Match(Encoding.UTF8.GetString(buffer)).Groups[1].Value.Trim();
byte[] keyBytes = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(key + guid));
key = Convert.ToBase64String(keyBytes);
var response = string.Format(string.Join("\r\n", new string[]{
"HTTP/1.1 101 Switching Protocols",
"Upgrade: websocket",
"Connection: Upgrade",
"Sec-WebSocket-Accept: {0}",
"",
""
}), key);
Socket.Send(Encoding.UTF8.GetBytes(response));
}
catch (SocketException ex)
{
Console.WriteLine("{0}: {1}", "SendHandshake", ex.Message);
Close();
}
catch (Exception ex)
{
Console.WriteLine("{0}: {1}", "SendHandshake", ex.Message);
}
}
private void BeginReceive()
{
try
{
Buffer = new byte[BufferSize];
Socket.BeginReceive(Buffer, 0, BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), this);
}
catch (SocketException ex)
{
Console.WriteLine("{0}: {1}", "BeginReceive1", ex.Message);
Close();
}
catch (Exception ex)
{
Console.WriteLine("{0}: {1}", "BeginReceive2", ex.Message);
}
}
public static byte[] TrimEnd(byte[] array)
{
int lastIndex = Array.FindLastIndex(array, b => b != 0);
Array.Resize(ref array, lastIndex + 1);
return array;
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
var bytes = new byte[0];
if (decoder.indexFirstMask==0)
{
bytes = decoder.DecodeMessage(TrimEnd(Buffer));
}
else
{
bytes = decoder.DecodeRemainingMessage(TrimEnd(Buffer));
}
Buffers = Buffers.Concat(bytes).ToList();
if (Buffers.ToArray().Length == Convert.ToInt32(fileInfo["size"]))
{
File.WriteAllBytes(#"C:\" + fileInfo["name"], Buffers.ToArray());
}
BeginReceive();
}
catch (SocketException ex)
{
Close();
Console.WriteLine("{0}: {1}", "ReceiveCallback", ex.Message);
}
catch (Exception ex)
{
BeginReceive();
Console.WriteLine("{0}: {1}", "ReceiveCallback", ex.Message);
}
}
public void Close()
{
Socket.Shutdown(SocketShutdown.Both);
Socket.Close();
Socket.Dispose();
}
}
when file size less than 50kb, byte 0 is 130 (opcode=2) and file size larger than 50 kb, byte 0 is 2;
It is clear that there is a problem with frame decoding or handshake response....
public class BytesDecoder
{
public int dataLength, indexFirstMask=0;
public IEnumerable<byte> keys;
public byte[] DecodeMessage(byte[] bytes)
{
Console.WriteLine("+DecodeMessage+");
byte secondByte = bytes[1];
bool masked = (bytes[1] & 128) != 0;
dataLength = secondByte & 127;
indexFirstMask = 2;
if (masked)
{
Console.WriteLine("Masked bit SET");
}
if (dataLength == 126)
{
indexFirstMask = 4;
dataLength = bytes[3] | bytes[2] << 8;
}
else if (dataLength == 127)
{
indexFirstMask = 10;
dataLength = bytes[9] | bytes[8] << 8 | bytes[7] << 16 | bytes[6] << 24 | bytes[5] << 32 |
bytes[4] << 40 | bytes[3] << 48 | bytes[2] << 56;
}
keys = bytes.Skip(indexFirstMask).Take(4);
int indexFirstDataByte = indexFirstMask + 4;
byte[] decoded = new byte[bytes.Length - indexFirstDataByte];
Console.WriteLine("dataLength : " + dataLength + " ; bytes.Length : " + bytes.Length);
int j = 0;
for (int i = indexFirstDataByte; i < bytes.Length; i++)
{
decoded[j] = (byte)(bytes[i] ^ keys.ElementAt(j % 4));
j++;
}
Console.WriteLine("-DecodeMessage-");
return decoded;
}
public byte[] DecodeRemainingMessage(byte[] bytes)
{
Console.WriteLine("+DecodeRemainingMessage+");
int indexFirstDataByte = 0;
byte[] decoded = new byte[bytes.Length - indexFirstDataByte];
int j = 0;
for (int i = indexFirstDataByte; i < bytes.Length; i++)
{
decoded[j] = (byte)(bytes[i] ^ keys.ElementAt(j % 4));
j++;
}
Console.WriteLine("-DecodeRemainingMessage-");
return decoded;
}
}
js code:
connect:function(){
var root = this;
root.websocket = new WebSocket(root.url);
root.websocket.binaryType = "arraybuffer";
root.websocket.onopen = () => root.fireEvent('connect');
root.websocket.onmessage = (e) => {
root.fireEvent('receive',JSON.parse(e.data));
}
window.addEventListener("beforeunload", function() {
root.websocket.onclose = function () {};
root.websocket.close();
});
},
sendMessage:function(msg){
var root = this;
root.controller.websocket.send(message);
},
sendFile:function(){
var root = this;
var file = document.getElementById('filename').files[0];
var root = this;
var loader = new FileReader();
loader.onload = (e) => {
var byteArray = new Uint8Array(e.target.result);
root.buffer = byteArray.buffer;
root.byteLength = root.buffer.byteLength;
root.server.websocket.send(root.buffer);
}
loader.readAsArrayBuffer(file);
}
Edit 1:
problem found large messages dataLength not currect
dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0);
for example file size is 169174 received data length
received data length in firefox= 169174
received data length in chrome= 131000
Edit 2:
FIN bit in chrome is 2 and in firefox is 1
Faced a problem. There is a 0x03 function, but I need to redo it in 0x06, I don’t understand how to do it.
I know that function 06 does not have a variable part. The value of one register is always transferred to it. But I can not understand what needs to be corrected. Please, help.
Here I create a package for functions:
private void BuildMessage(byte address, byte type, ushort start, ushort registers, ref byte[] message)
{
if (type == 3 || type == 16)
{
//Array to receive CRC bytes:
byte[] CRC = new byte[2];
message[0] = address;
message[1] = type;
message[2] = (byte)(start >> 8);
message[3] = (byte)start;
message[4] = (byte)(registers >> 8);
message[5] = (byte)registers;
GetCRC(message, ref CRC);
message[message.Length - 2] = CRC[0];
message[message.Length - 1] = CRC[1];
}
else if (type == 6)
{
byte[] CRC = new byte[2];
message[0] = address;
message[1] = type;
message[2] = (byte)(start >> 8);
message[3] = (byte)start;
message[4] = (byte)(registers >> 8);
message[5] = (byte)registers;
GetCRC(message, ref CRC);
message[6] = CRC[0];
message[7] = CRC[1];
}
}
This is my function number 3:
public bool SendFunc(int funcNumer, string connectType, byte address, ushort start, ushort registers, ref short[] values)
{
#region №3
if (funcNumer == 3)
{
#region serial-port
if (connectType.Equals("COM"))
{
//Ensure port is open:
if (sp.IsOpen)
{
//Clear in/out buffers:
sp.DiscardOutBuffer();
sp.DiscardInBuffer();
//Function 3 request is always 8 bytes:
byte[] message = new byte[8];
//Function 3 response buffer:
byte[] response = new byte[5 + 2 * registers];
//Build outgoing modbus message:
BuildMessage(address, (byte)3, start, registers, ref message);
//Send modbus message to Serial Port:
try
{
sp.Write(message, 0, message.Length);
GetResponse("COM", ref response);
}
catch (Exception err)
{
modbusStatus = "" + err.Message;
return false;
}
//Evaluate message:
if (CheckResponse(response))
{
//Return requested register values:
for (int i = 0; i < (response.Length - 5) / 2; i++)
{
values[i] = response[2 * i + 3];
values[i] <<= 8;
values[i] += response[2 * i + 4];
}
modbusStatus = "";
return true;
}
else
{
modbusStatus = "";
return false;
}
}
else
{
modbusStatus = "";
return false;
}
}
And here I am trying to implement function number 6:
if (funcNumer == 6)
{
#region serial-port
if (connectType.Equals("COM"))
{
//Ensure port is open:
if (sp.IsOpen)
{
//Clear in/out buffers:
sp.DiscardOutBuffer();
sp.DiscardInBuffer();
//Function 3 request is always 8 bytes:
byte[] message = new byte[8];
//Function 3 response buffer:
byte[] response = new byte[5 + 2 * registers];
//Build outgoing modbus message:
BuildMessage(address, (byte)6, start, registers, ref message);
//Send modbus message to Serial Port:
try
{
sp.Write(message, 0, message.Length);
GetResponse("COM", ref response);
}
catch (Exception err)
{
modbusStatus = "" + err.Message;
return false;
}
//Evaluate message:
if (CheckResponse(response))
{
//Return requested register values:
for (int i = 0; i < (response.Length - 5) / 2; i++)
{
values[i] = response[2 * i + 3];
values[i] <<= 8;
values[i] += response[2 * i + 4];
}
modbusStatus = "";
return true;
}
else
{
modbusStatus = "";
return false;
}
}
else
{
modbusStatus = "";
return false;
}
}
This is my function to check the response:
private bool CheckResponse(byte[] response)
{
//Perform a basic CRC check:
byte[] CRC = new byte[2];
GetCRC(response, ref CRC);
if (CRC[0] == response[response.Length - 2] && CRC[1] == response[response.Length - 1])
return true;
else
return false;
}
This is my function for get response:
private void GetResponse(string connect, ref byte[] response)
{
if (connect.Equals("COM"))
{
for (int i = 0; i < response.Length; i++)
{
response[i] = (byte)(sp.ReadByte());
}
}
else if (connect.Equals("TCP"))
{
NetworkStream stream = tcpClient.GetStream();
for (int i = 0; i < response.Length; i++)
{
response[i] = (byte)(stream.ReadByte());
}
}
else
{
}
}
The difference between Modbus function 0x03 and 0x06 is actually the part of the code you did not show on your question: CheckResponse(response).
Function 0x03 reads a number of registers, and the values in those registers (coming from the slave) are included in the response.
Function 0x06 writes a single register and gives an echo back. So the response is the same as the query.
With that information, it should be easy to modify your code: remove the for loop where you retrieve the register values.
Other than that you might need to modify your CheckResponse() function too, but that should also be quite straight forward: just check the response is exactly the same as the query (message).
EDIT: if your CheckResponse()` function only checks for the correct CRC on the responses I suppose you can keep it as it is.
My game has two clients & one server. client1 sends data to client2 by placing data on server & client2 retrieving from server. Similarly client2 sends data to client1 via server.
When I send data from Client1 to client2 everything is fine but when client2 sends data to client1 it receives irrelevant data.
What I believe is during first communication, whatever data is on server is not clearing & so while Clien2 is sending data back to client1, the data overlaps with the data on server & sends irrelevant data.
I am using same code for all client but multiple instances of client code.
Below is the code I am using :-
private void OnKeyDown1(object sender, KeyEventArgs e)
{
tbxno = 1;
if (e.Key == Key.Enter || e.Key == Key.Return)
{
// Disable further edit on textbox
playerTbx1.IsReadOnly = true;
// Compare answers entered by user
char[] letters = new char[playerTbx1.Text.Length];
StringReader sr = new StringReader(playerTbx1.Text);
sr.Read(letters, 0, playerTbx1.Text.Length);
int c = 0;
Console.WriteLine(word[i - 1]);
string label = MainWordLabel.Content.ToString();
while (label.Contains(letters[c].ToString()))
{
c++;
if (c == letters.Length)
break;
}
int count = c;
MessageBox.Show("No. of words matching : " + c);
// Score calculation
score += c * 5;
PlayerScoreTbx.Text = score.ToString();
//Send data to server
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(playerTbx1.Text);
byte[] length = new byte[2];
length[0] = (byte)(playerTbx1.Text.Length % 256);
length[1] = (byte)(playerTbx1.Text.Length / 256);
byte[] usernameBuffer = System.Text.Encoding.ASCII.GetBytes(Username_Textbox.Text);
byte[] unamelength = new byte[2];
unamelength[0] = (byte)(Username_Textbox.Text.Length % 256);
unamelength[1] = (byte)(Username_Textbox.Text.Length / 256);
byte[] scoreBuffer = System.Text.Encoding.ASCII.GetBytes(PlayerScoreTbx.Text);
byte[] scoreLength = new byte[2];
scoreLength[0] = (byte)(PlayerScoreTbx.Text.Length % 256);
scoreLength[1] = (byte)(PlayerScoreTbx.Text.Length / 256);
byte[] tno = new byte[1];
tno[0] = (byte)tbxno;
tcpClient.Client.Send(length);
tcpClient.Client.Send(buffer);
tcpClient.Client.Send(unamelength);
tcpClient.Client.Send(usernameBuffer);
tcpClient.Client.Send(scoreLength);
tcpClient.Client.Send(scoreBuffer);
tcpClient.Client.Send(tno);
}
}
while recieving data back from server :-
void getdata()
{
while (tcpsocket.Connected)
{
NetworkStream stream = tcpClient.GetStream();
byte[] userName = new byte[1024];
byte[] buffer = new byte[256];
byte[] length = new byte[2];
byte[] userlength = new byte[2];
byte[] scoreBuffer = new byte[1024];
byte[] scoreLength = new byte[2];
// read length of username
int readUserlength = stream.Read(userlength, 0, 2);
int userLength = userlength[0] + (userlength[1] * 256);
// read username
stream.Read(userName, 0, userLength);
string userReceived = System.Text.Encoding.ASCII.GetString(userName, 0, userLength);
// read length of score
int readUScore = stream.Read(scoreLength, 0, 2);
int lengthOfScore = scoreLength[0] + (scoreLength[1] * 256);
// read score
stream.Read(scoreBuffer, 0, lengthOfScore);
string score = System.Text.Encoding.ASCII.GetString(scoreBuffer, 0, lengthOfScore);
// check if the same client is not receiving data
this.Dispatcher.Invoke(() =>
{
if (userReceived.Equals(Username_Textbox.Text) == false)
{
int readlength = stream.Read(length, 0, 2);
int dataLength = length[0] + (length[1] * 256);
stream.Read(buffer, 0, dataLength);
string data = System.Text.Encoding.ASCII.GetString(buffer,0,dataLength);
byte[] tbox = new byte[1];
stream.Read(tbox, 0, 1);
int count = tbox[0];
this.Dispatcher.Invoke(() =>
{
OppTbx1.Text = data;
OppScoreTbx.Text = score;
});
}
});
}
}
Codes on Server :-
public delegate void Disconnected(ClientManager item);
public delegate void MessageReceived(string item, string username, string score, byte textbox);
public class ClientManager
{
public Socket socket { get; set; }
private NetworkStream networkStream;
public event Disconnected OnDisconnected;
public event MessageReceived OnMessage;
public ClientManager(Socket clientSocket)
{
this.socket = clientSocket;
this.networkStream = new NetworkStream(this.socket);
Task.Factory.StartNew(() =>
{
while (socket.Connected)
{
// TextBox Data
byte[] dataBuffer = new byte[1024];
byte[] length = new byte[2];
int readlength = this.networkStream.Read(length, 0, 2);
if (readlength == 0)
break;
int dataLength = length[0] + (length[1] * 256);
int readDataBytes = this.networkStream.Read(dataBuffer, 0, dataLength);
if (readDataBytes == 0)
break;
string data = System.Text.Encoding.ASCII.GetString(dataBuffer, 0, dataLength);
// Username Information
byte[] username = new byte[1024];
byte[] userlength = new byte[2];
int readLengthbytes = this.networkStream.Read(userlength, 0, 2);
if (readLengthbytes == 0)
break;
int userLength = userlength[0] + (userlength[1] * 256);
int readUsername = this.networkStream.Read(username, 0, userLength);
if (readUsername == 0)
break;
string usernameString = System.Text.Encoding.ASCII.GetString(username, 0, userLength);
// score
byte[] scoreBuffer = new byte[1024];
byte[] scorLength = new byte[2];
int readscorelength = this.networkStream.Read(scorLength, 0, 2);
if (readscorelength == 0)
break;
int lenghtOfScore = scorLength[0] + (scorLength[1] * 256);
int readscore = this.networkStream.Read(scoreBuffer, 0, lenghtOfScore);
if (readscore == 0)
break;
string score = System.Text.Encoding.ASCII.GetString(scoreBuffer, 0, lenghtOfScore);
byte[] tbx = new byte[1];
this.networkStream.Read(tbx,0,1);
//send data,sername & score
OnMessage(data, usernameString, score, tbx[0]);
}
//socket.Disconnect(false);
if (OnDisconnected != null)
OnDisconnected(this);
});
}
}
}
sending to client back :-
void SendChatMessage(string message, string username,string score, byte tbox)
{
byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
byte[] length = new byte[2];
length[0] = (byte)(message.Length % 256);
length[1] = (byte)(message.Length / 256);
byte[] usernameBuffer = System.Text.Encoding.ASCII.GetBytes(username);
byte[] userlength = new byte[2];
userlength[0] = (byte)(username.Length % 256);
userlength[1] = (byte)(username.Length / 256);
byte[] scoreBuffer = System.Text.Encoding.ASCII.GetBytes(score);
byte[] scoreLength = new byte[2];
scoreLength[0] = (byte)(score.Length % 256);
scoreLength[1] = (byte)(score.Length / 256);
byte[] tbxCount = new byte[1];
tbxCount[0] = tbox;
Task.Factory.StartNew(() =>
{
lock (tcpClients)
{
foreach (var item in tcpClients)
{
try
{
item.socket.Send(userlength);
item.socket.Send(usernameBuffer);
item.socket.Send(scoreLength);
item.socket.Send(scoreBuffer);
item.socket.Send(length);
item.socket.Send(data);
item.socket.Send(tbxCount);
}
catch (Exception ex)
{
ex.GetBaseException();
}
}
}
});
}
}
As invarably seen in such questions, you are ignoring the count returned by Read() and assuming that the read filled the buffer. It isn't specified to do that, or to transfer any more than one byte: zero bytes in non-blocking mode. You need to loop until you have all the data expected or required.
I am trying to retrieve an uploaded file from my webserver. As the client sends its files through a webform (random files), I need to parse the request to get the file out and to process it further on.
Basically, the code goes as:
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
StreamReader r = new StreamReader(request.InputStream, System.Text.Encoding.Default);
// this is the retrieved file from streamreader
string file = null;
while ((line = r.ReadLine()) != null){
// i read the stream till i retrieve the filename
// get the file data out and break the loop
}
// A byststream is created by converting the string,
Byte[] bytes = request.ContentEncoding.GetBytes(file);
MemoryStream mstream = new MemoryStream(bytes);
// do the rest
As a result, i am able to retrieve textfiles, but for all other files, they are corrupted.
Could someone tell me how to parse these HttplistnerRequests properly (or providing a lightweighted alternative)?
I think you are making things harder on yourself than necessary by doing this with an HttpListener rather than using the built in facilities of ASP.Net. But if you must do it this way here is some sample code. Note: 1) I'm assuming you're using enctype="multipart/form-data" on your <form>. 2) This code is designed to be used with a form containing only your <input type="file" /> if you want to post other fields or multiple files you'll have to change the code. 3) This is meant to be a proof of concept/example, it may have bugs, and is not particularly flexible.
static void Main(string[] args)
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8080/ListenerTest/");
listener.Start();
HttpListenerContext context = listener.GetContext();
SaveFile(context.Request.ContentEncoding, GetBoundary(context.Request.ContentType), context.Request.InputStream);
context.Response.StatusCode = 200;
context.Response.ContentType = "text/html";
using (StreamWriter writer = new StreamWriter(context.Response.OutputStream, Encoding.UTF8))
writer.WriteLine("File Uploaded");
context.Response.Close();
listener.Stop();
}
private static String GetBoundary(String ctype)
{
return "--" + ctype.Split(';')[1].Split('=')[1];
}
private static void SaveFile(Encoding enc, String boundary, Stream input)
{
Byte[] boundaryBytes = enc.GetBytes(boundary);
Int32 boundaryLen = boundaryBytes.Length;
using (FileStream output = new FileStream("data", FileMode.Create, FileAccess.Write))
{
Byte[] buffer = new Byte[1024];
Int32 len = input.Read(buffer, 0, 1024);
Int32 startPos = -1;
// Find start boundary
while (true)
{
if (len == 0)
{
throw new Exception("Start Boundaray Not Found");
}
startPos = IndexOf(buffer, len, boundaryBytes);
if (startPos >= 0)
{
break;
}
else
{
Array.Copy(buffer, len - boundaryLen, buffer, 0, boundaryLen);
len = input.Read(buffer, boundaryLen, 1024 - boundaryLen);
}
}
// Skip four lines (Boundary, Content-Disposition, Content-Type, and a blank)
for (Int32 i = 0; i < 4; i++)
{
while (true)
{
if (len == 0)
{
throw new Exception("Preamble not Found.");
}
startPos = Array.IndexOf(buffer, enc.GetBytes("\n")[0], startPos);
if (startPos >= 0)
{
startPos++;
break;
}
else
{
len = input.Read(buffer, 0, 1024);
}
}
}
Array.Copy(buffer, startPos, buffer, 0, len - startPos);
len = len - startPos;
while (true)
{
Int32 endPos = IndexOf(buffer, len, boundaryBytes);
if (endPos >= 0)
{
if (endPos > 0) output.Write(buffer, 0, endPos-2);
break;
}
else if (len <= boundaryLen)
{
throw new Exception("End Boundaray Not Found");
}
else
{
output.Write(buffer, 0, len - boundaryLen);
Array.Copy(buffer, len - boundaryLen, buffer, 0, boundaryLen);
len = input.Read(buffer, boundaryLen, 1024 - boundaryLen) + boundaryLen;
}
}
}
}
private static Int32 IndexOf(Byte[] buffer, Int32 len, Byte[] boundaryBytes)
{
for (Int32 i = 0; i <= len - boundaryBytes.Length; i++)
{
Boolean match = true;
for (Int32 j = 0; j < boundaryBytes.Length && match; j++)
{
match = buffer[i + j] == boundaryBytes[j];
}
if (match)
{
return i;
}
}
return -1;
}
To help you better understand what the code above is doing, here is what the body of the HTTP POST looks like:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9lcB0OZVXSqZLbmv
------WebKitFormBoundary9lcB0OZVXSqZLbmv
Content-Disposition: form-data; name="my_file"; filename="Test.txt"
Content-Type: text/plain
Test
------WebKitFormBoundary9lcB0OZVXSqZLbmv--
I've left out the irrelevant headers. As you can see, you need to parse the body by scanning through to find the beginning and ending boundary sequences, and drop the sub headers that come before the content of your file. Unfortunately you cannot use StreamReader because of the potential for binary data. Also unfortunate is the fact that there is no per file Content-Length (the Content-Length header for the request specifies the total length of the body including boundaries, sub-headers, and spacing.
You can't use StreamReader because it is meant to read streams in which the bytes are in the UTF8. Instead, you want to read the contents of the stream to a receive buffer, remove all the stuff you don't need, get the file extension of the uploaded file, extract the contents of the uploaded file, then save the file contents to a new file. The code I show in this post assumes your form looks like this:
<form enctype="multipart/form-data" method="POST" action="/uploader">
<input type="file" name="file">
<input type="submit">
</form>
As you can see, the code is only meant to handle a form that only has the file. Since there is no way to extract the contents of a file on the server from a application/x-www-form-urlencoded form, so you have to include the "multipart/form-data".
First, for this method of handling uploaded files, you will first need this little bit of code:
using System;
using System.IO;
using System.Text;
using System.Net;
using System.Collections.Generic;
Second, you need to read the contents of the request.InputStream to a receive buffer, or a byte[]. We do this by making a byte[] buffer with the length of the Content-Length header sent by the browser. Then, we read the contents of the request.InputStream to the buffer. The code would look like this:
int len = int.Parse(request.Headers["Content-Length"]);
byte[] buffer = new byte[len];
request.InputStream.Read(buffer, 0, len);
The stream will look somewhat like this:
------WebKitFormBoundary9lcB0OZVXSqZLbmv
Content-Disposition: form-data; name="file"; filename="example-file.txt"
Content-Type: text/plain
file contents here
------WebKitFormBoundary9lcB0OZVXSqZLbmv--
Next, you need to get the file extension of the uploaded file. We can do this using this code:
string fileExtension = Encoding.UTF8.GetString(bytes).Split("\r\n")[1].Split("filename=\"")[1].Replace("\"", "").Split('.')[^1];
Then, we need to get the contents of the file. We do this by removing the stuff at the beginning (the -----WebKitFormBoundary, the Content-Disposition, the Content-Type, and a blank line), then removing the last line of the request body, plus an extra \r\n at the end. Here is the code that does just that:
// note that the variable buffer is the byte[], and the variable bytes is the List<byte>
string stringBuffer = Encoding.UTF8.GetString(buffer);
List<byte> bytes = new List<byte>(buffer);
string[] splitString = stringBuffer.Split('\n');
int lengthOfFourLines = splitString[0].Length + splitString[1].Length +
splitString[2].Length + splitString[3].Length + 4;
bytes.RemoveRange(0, lengthOfFourLines);
int lengthOfLastLine = splitString[^2].Length+2;
bytes.RemoveRange(bytes.Count - lengthOfLastLine, lengthOfLastLine);
buffer = bytes.ToArray();
Finally, we need to save the contents to a file. The code below generates a random file name with the user-specified file extension, and saves the files contents to it.
string fname = "";
string[] chars = "q w e r t y u i o p a s d f g h j k l z x c v b n m Q W E R T Y U I O P A S D F G H J K L Z X C V B N M 1 2 3 4 5 6 7 8 9 0".Split(" ");
for (int i = 0; i < 10; i++)
{
fname += chars[new Random().Next(chars.Length)];
}
fname += fileExtension;
FileStream file = File.Create(fname);
file.Write(buffer);
file.Close();
Here is the whole code put together:
public static void Main()
{
var listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8080/");
listener.Start();
while(true)
{
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
if(request.HttpMethod=="POST") SaveFile(request);
response.OutputStream.Write(Encoding.UTF8.GetBytes("file successfully uploaded"));
response.OutputStream.Close();
}
}
void SaveFile(HttpListenerRequest request)
{
int len = (int)request.ContentLength64;
Console.WriteLine(len);
byte[] buffer = new byte[len];
request.InputStream.Read(buffer, 0, len);
string stringBuffer = Encoding.UTF8.GetString(buffer);
Console.WriteLine(stringBuffer.Replace("\r\n","\\r\\n\n"));
string fileExtension = stringBuffer.Split("\r\n")[1]
.Split("filename=\"")[1]
.Replace("\"", "")
.Split('.')[^1]
;
List<byte> bytes = new List<byte>(buffer);
string[] splitString = stringBuffer.Split('\n');
int lengthOfFourLines = splitString[0].Length + splitString[1].Length + splitString[2].Length + splitString[3].Length + 4;
bytes.RemoveRange(0, lengthOfFourLines);
int lengthOfLastLine = splitString[^2].Length+2;
bytes.RemoveRange(bytes.Count - lengthOfLastLine, lengthOfLastLine);
buffer = bytes.ToArray();
string fname = "";
string[] chars = "q w e r t y u i o p a s d f g h j k l z x c v b n m Q W E R T Y U I O P A S D F G H J K L Z X C V B N M 1 2 3 4 5 6 7 8 9 0".Split(" ");
for (int i = 0; i < 10; i++)
{
fname += chars[new Random().Next(chars.Length)];
}
fname += "." + fileExtension;
FileStream file = File.Create(fname);
file.Write(buffer);
file.Close();
}
Also, if you want to send an uploaded file to the client, here is a useful function that sends the file to the client.
// Make sure you are using System.IO, and System.Net when making this function.
// Also make sure you set the content type of the response before calling this function.
// fileName is the name of the file you want to send to the client, and output is the response.OutputStream.
public static void SendFile(string fileName, Stream output)
{
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
fs.CopyTo(output);
fs.Close();
output.Close();
}
The problem is you are reading the file as text.
You need to read the file as a bytearray instead and using the BinaryReader is better and easier to use than StreamReader:
Byte[] bytes;
using (System.IO.BinaryReader r = new System.IO.BinaryReader(request.InputStream))
{
// Read the data from the stream into the byte array
bytes = r.ReadBytes(Convert.ToInt32(request.InputStream.Length));
}
MemoryStream mstream = new MemoryStream(bytes);
May have bugs, test thoroughly. This one gets all post, get, and files.
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
namespace DUSTLauncher
{
class HttpNameValueCollection
{
public class File
{
private string _fileName;
public string FileName { get { return _fileName ?? (_fileName = ""); } set { _fileName = value; } }
private string _fileData;
public string FileData { get { return _fileData ?? (_fileName = ""); } set { _fileData = value; } }
private string _contentType;
public string ContentType { get { return _contentType ?? (_contentType = ""); } set { _contentType = value; } }
}
private NameValueCollection _get;
private Dictionary<string, File> _files;
private readonly HttpListenerContext _ctx;
public NameValueCollection Get { get { return _get ?? (_get = new NameValueCollection()); } set { _get = value; } }
public NameValueCollection Post { get { return _ctx.Request.QueryString; } }
public Dictionary<string, File> Files { get { return _files ?? (_files = new Dictionary<string, File>()); } set { _files = value; } }
private void PopulatePostMultiPart(string post_string)
{
var boundary_index = _ctx.Request.ContentType.IndexOf("boundary=") + 9;
var boundary = _ctx.Request.ContentType.Substring(boundary_index, _ctx.Request.ContentType.Length - boundary_index);
var upper_bound = post_string.Length - 4;
if (post_string.Substring(2, boundary.Length) != boundary)
throw (new InvalidDataException());
var raw_post_strings = new List<string>();
var current_string = new StringBuilder();
for (var x = 4 + boundary.Length; x < upper_bound; ++x)
{
if (post_string.Substring(x, boundary.Length) == boundary)
{
x += boundary.Length + 1;
raw_post_strings.Add(current_string.ToString().Remove(current_string.Length - 3, 3));
current_string.Clear();
continue;
}
current_string.Append(post_string[x]);
var post_variable_string = current_string.ToString();
var end_of_header = post_variable_string.IndexOf("\r\n\r\n");
if (end_of_header == -1) throw (new InvalidDataException());
var filename_index = post_variable_string.IndexOf("filename=\"", 0, end_of_header);
var filename_starts = filename_index + 10;
var content_type_starts = post_variable_string.IndexOf("Content-Type: ", 0, end_of_header) + 14;
var name_starts = post_variable_string.IndexOf("name=\"") + 6;
var data_starts = end_of_header + 4;
if (filename_index == -1) continue;
var filename = post_variable_string.Substring(filename_starts, post_variable_string.IndexOf("\"", filename_starts) - filename_starts);
var content_type = post_variable_string.Substring(content_type_starts, post_variable_string.IndexOf("\r\n", content_type_starts) - content_type_starts);
var file_data = post_variable_string.Substring(data_starts, post_variable_string.Length - data_starts);
var name = post_variable_string.Substring(name_starts, post_variable_string.IndexOf("\"", name_starts) - name_starts);
Files.Add(name, new File() { FileName = filename, ContentType = content_type, FileData = file_data });
continue;
}
}
private void PopulatePost()
{
if (_ctx.Request.HttpMethod != "POST" || _ctx.Request.ContentType == null) return;
var post_string = new StreamReader(_ctx.Request.InputStream, _ctx.Request.ContentEncoding).ReadToEnd();
if (_ctx.Request.ContentType.StartsWith("multipart/form-data"))
PopulatePostMultiPart(post_string);
else
Get = HttpUtility.ParseQueryString(post_string);
}
public HttpNameValueCollection(ref HttpListenerContext ctx)
{
_ctx = ctx;
PopulatePost();
}
}
}
I like #paul-wheeler answer. However I needed to modify their code to include some additional data (In this case, the directory structure).
I'm using this code to upload files:
var myDropzone = $("#fileDropZone");
myDropzone.dropzone(
{
url: "http://" + self.location.hostname + "/Path/Files.html,
method: "post",
createImageThumbnails: true,
previewTemplate: document.querySelector('#previewTemplateId').innerHTML,
clickable: false,
init: function () {
this.on('sending', function(file, xhr, formData){
// xhr is XMLHttpRequest
var name = file.fullPath;
if (typeof (file.fullPath) === "undefined") {
name = file.name;
}
formData.append('fileNameWithPath', name);
});
}
});
Here is #paul-wheeler modified code. Thanks #paul-wheeler.
public class FileManager
{
public static void SaveFile(HttpListenerRequest request, string savePath)
{
var tempFileName = Path.Combine(savePath, $"{DateTime.Now.Ticks}.tmp");
if (!Directory.Exists(savePath))
{
Directory.CreateDirectory(savePath);
}
var (res, fileName) = SaveTmpFile(request, tempFileName);
if (res)
{
var filePath = Path.Combine(savePath, fileName);
var fileDir = filePath.Substring(0, filePath.LastIndexOf(Path.DirectorySeparatorChar));
if (!Directory.Exists(fileDir))
{
Directory.CreateDirectory(fileDir);
}
if (File.Exists(filePath))
{
File.Delete(filePath);
}
File.Move(tempFileName, filePath);
}
}
private static (bool, string) SaveTmpFile(HttpListenerRequest request, string tempFileName)
{
var enc = request.ContentEncoding;
var boundary = GetBoundary(request.ContentType);
var input = request.InputStream;
byte[] boundaryBytes = enc.GetBytes(boundary);
var boundaryLen = boundaryBytes.Length;
using (FileStream output = new FileStream(tempFileName, FileMode.Create, FileAccess.Write))
{
var buffer = new byte[1024];
var len = input.Read(buffer, 0, 1024);
var startPos = -1;
// Get file name and relative path
var strBuffer = Encoding.Default.GetString(buffer);
var strStart = strBuffer.IndexOf("fileNameWithPath") + 21;
if (strStart < 21)
{
Logger.LogError("File name not found");
return (false, null);
}
var strEnd = strBuffer.IndexOf(boundary, strStart) - 2;
var fileName = strBuffer.Substring(strStart, strEnd - strStart);
fileName = fileName.Replace('/', Path.DirectorySeparatorChar);
// Find start boundary
while (true)
{
if (len == 0)
{
Logger.LogError("Find start boundary not found");
return (false, null);
}
startPos = IndexOf(buffer, len, boundaryBytes);
if (startPos >= 0)
{
break;
}
else
{
Array.Copy(buffer, len - boundaryLen, buffer, 0, boundaryLen);
len = input.Read(buffer, boundaryLen, 1024 - boundaryLen);
}
}
// Advance to data
var foundData = false;
while (!foundData)
{
while (true)
{
if (len == 0)
{
Logger.LogError("Preamble not Found");
return (false, null);
}
startPos = Array.IndexOf(buffer, enc.GetBytes("\n")[0], startPos);
if (startPos >= 0)
{
startPos++;
break;
}
else
{
// In case read in line is longer than buffer
len = input.Read(buffer, 0, 1024);
}
}
var currStr = Encoding.Default.GetString(buffer).Substring(startPos);
if (currStr.StartsWith("Content-Type:"))
{
// Go past the last carriage-return\line-break. (\r\n)
startPos = Array.IndexOf(buffer, enc.GetBytes("\n")[0], startPos) + 3;
break;
}
}
Array.Copy(buffer, startPos, buffer, 0, len - startPos);
len = len - startPos;
while (true)
{
var endPos = IndexOf(buffer, len, boundaryBytes);
if (endPos >= 0)
{
if (endPos > 0) output.Write(buffer, 0, endPos - 2);
break;
}
else if (len <= boundaryLen)
{
Logger.LogError("End Boundaray Not Found");
return (false, null);
}
else
{
output.Write(buffer, 0, len - boundaryLen);
Array.Copy(buffer, len - boundaryLen, buffer, 0, boundaryLen);
len = input.Read(buffer, boundaryLen, 1024 - boundaryLen) + boundaryLen;
}
}
return (true, fileName);
}
}
private static int IndexOf(byte[] buffer, int len, byte[] boundaryBytes)
{
for (int i = 0; i <= len - boundaryBytes.Length; i++)
{
var match = true;
for (var j = 0; j < boundaryBytes.Length && match; j++)
{
match = buffer[i + j] == boundaryBytes[j];
}
if (match)
{
return i;
}
}
return -1;
}
private static string GetBoundary(string ctype)
{
return "--" + ctype.Split(';')[1].Split('=')[1];
}
}
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
var contentType = MediaTypeHeaderValue.Parse(context.Request.ContentType);
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value;
var multipartReader = new MultipartReader(boundary, context.Request.InputStream);
var section = (await multipartReader.ReadNextSectionAsync()).AsFileSection();
var fileName = section.FileName;
var fileStream = section.FileStream;
for a given HttpListenerRequest req and a string path the following code saves a file (worked for text file but also png file)
int len = (int)req.ContentLength64;
byte[] buffer = new byte[len];
int totalRead = 0;
while(totalRead < len){
// InputStream.Read does not read always read full stream (so loop until it has)
totalRead += req.InputStream.Read(buffer, totalRead, len - totalRead);
}
string stringBuffer = Encoding.UTF8.GetString(buffer);
string startTag = stringBuffer.Substring(0, stringBuffer.IndexOf("\r\n\r\n") + 4);
string endTag = stringBuffer.Substring(stringBuffer.IndexOf("\r\n------WebKitFormBoundary"));
List<byte> bytes = new List<byte>(buffer);
bytes = bytes.GetRange(startTag.Length, len - (startTag.Length + endTag.Length));
buffer = bytes.ToArray();
File.WriteAllBytes(path, buffer);
(build on top of copee moo solution)