I want to add echo to wave file, in order to do so I need to open it and then save it first. I've written some methods but output file is not correct.
Testing input file size: 731 014
Output file sieze: 730 898
But the "space on disc" is the same for both files, which is 733 184
Error code or whatever it is displayed when opened: 0xc00d36c4
What can be the issue here? This looks pretty simple and yet it's not working. Here's my header, read and write methods:
class WaveFile
{
struct WaveHeader
{
public byte[] RiffID;
public uint fileSize;
public byte[] format;
//Wave format chunk 1
public byte[] fmtID;
public uint fmtSize;
public ushort audioFormat;
public ushort channels;
public uint sampleRate;
public uint byteRate;
public ushort blockAlign;
public int bitsPerSample;
//Wave format chunk 2
public byte[] dataID;
public uint dataSize;
}
uint samples;
public List<short> L;
public List<short> R;
WaveHeader header = new WaveHeader();
//loading file, preparation for modyfying
public bool loadWaveFile(string filePath)
{
using (FileStream fs = File.Open(filePath, FileMode.Open))
using (BinaryReader reader = new BinaryReader(fs))
{
// chunk 0
header.RiffID = reader.ReadBytes(4);
header.fileSize = reader.ReadUInt32();
header.format = reader.ReadBytes(4);
// chunk 1
header.fmtID = reader.ReadBytes(4);
header.fmtSize = reader.ReadUInt32();
header.audioFormat = reader.ReadUInt16();
header.channels = reader.ReadUInt16();
header.sampleRate = reader.ReadUInt32();
header.byteRate = reader.ReadUInt32();
header.blockAlign = reader.ReadUInt16();
header.bitsPerSample = reader.ReadInt16();
// chunk 2
header.dataID = reader.ReadBytes(4);
header.dataSize = reader.ReadUInt32();
// DATA is stereo
L = new List<short>();
R = new List<short>();
samples = header.dataSize / header.blockAlign;
for (int i = 0; i < samples; i++)
{
L.Add((short)reader.ReadUInt16());
R.Add((short)reader.ReadUInt16());
}
reader.Close();
fs.Close();
}
return true;
}
public bool addEcho(int threadsNumber, int echoesNumber, int delay, int attenuation)
{
return true;
}
public bool saveWaveFile(string savingPath)
{
using (FileStream fs = new FileStream(#savingPath + "\\echo.wav", FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fs))
{
//chunk 0
writer.Write(header.RiffID);
writer.Write(header.fileSize);
writer.Write(header.format);
//chunk 1
writer.Write(header.fmtID);
writer.Write(header.fmtSize);
writer.Write(header.audioFormat);
writer.Write(header.channels);
writer.Write(header.sampleRate);
writer.Write(header.byteRate);
writer.Write(header.blockAlign);
writer.Write(header.bitsPerSample);
//chunk 2
writer.Write(header.dataID);
writer.Write(header.dataSize);
for (int i = 0; i < samples; i++)
{
writer.Write(L[i]);
writer.Write(R[i]);
}
writer.Close();
fs.Close();
return true;
}
}
}
I didn't find out what the issue was, but for echo purposes this class will work:
Class WaveFile
{
byte[] byteArray;
public void loadWaveFile(string filePath)
{
byteArray = File.ReadAllBytes(filePath);
}
public bool addEcho(int threadsNumber, int echoesNumber, int delay, int attenuation)
{
return true;
}
public bool saveWaveFile(string savingPath)
{
using (FileStream fs = new FileStream(#savingPath + "\\echo.wav", FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fs))
{
writer.Write(byteArray);
writer.Close();
fs.Close();
return true;
}
}
}
I am beginner c#.
I would like to make program converting from .wav file to .raw file.
I found some source and I would like to use it.
but something happened in the code. the error is related to ObjectDisposedException.
could you give me a some code or idea for me?
the whole code is under
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace WaveTestRead
{
class WaveReader
{
FileInfo m_fInfo;
FileStream m_fStream;
BinaryReader m_binReader;
// RIFF chunk
byte[] chunkID;
UInt32 chunkSize;
byte[] format;
// fmt subchunk
byte[] fmtChunkID;
UInt32 fmtChunkSize;
UInt16 audioFormat;
UInt16 numChannels;
UInt32 sampleRate;
UInt32 byteRate;
UInt16 blockAssign;
UInt16 BitsPerSample;
// data subchunk
byte[] dataChunkID;
UInt32 dataChunkSize;
byte[] data8L; // 8-bit left channel
byte[] data8R; // 8-bit right channel
Int16[] data16L; // 16-bit left channel
Int16[] data16R; // 16-bit right channel
int numSamples;
public WaveReader()
{
}
public bool Open(String filename)
{
string str;
m_fInfo = new FileInfo(filename);
m_fStream = m_fInfo.OpenRead();
m_binReader = new BinaryReader(m_fStream);
chunkID = new byte[4];
format = new byte[4];
chunkID = m_binReader.ReadBytes(4);
chunkSize = m_binReader.ReadUInt32();
format = m_binReader.ReadBytes(4);
str = System.Text.ASCIIEncoding.ASCII.GetString(chunkID, 0, 4);
if (str != "RIFF")
return false;
str = System.Text.ASCIIEncoding.ASCII.GetString(format, 0, 4);
if (str != "WAVE")
return false;
if (ReadFmt() == false)
return false;
if (ReadData() == false)
return false;
m_fStream.Close();
return true;
}
private bool ReadFmt()
{
fmtChunkID = new byte[4];
fmtChunkID = m_binReader.ReadBytes(4);
string str = System.Text.ASCIIEncoding.ASCII.GetString(fmtChunkID, 0, 4);
if (str != "fmt ")
return false;
fmtChunkSize = m_binReader.ReadUInt32();
audioFormat = m_binReader.ReadUInt16();
numChannels = m_binReader.ReadUInt16();
sampleRate = m_binReader.ReadUInt32();
byteRate = m_binReader.ReadUInt32();
blockAssign = m_binReader.ReadUInt16();
BitsPerSample = m_binReader.ReadUInt16();
return true;
}
static void Main(string[] args)
{
p.Open("wavetest.wav");
bool a = p.ReadFmt();
p.ReadData();
}
}
}
The code posted is not very well written.
Anyway you can try with these quick changes:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace WaveTestRead
{
public class Program
{
public static void Main(string[] args)
{
using (var waveReader = new WaveReader())
{
if (!waveReader.Open("wavetest.wav"))
{
Console.WriteLine("Failed to read file.");
return;
}
if (!waveReader.ReadFmt())
{
Console.WriteLine("Failed to read fmt.");
return;
}
// this method is not defined...
//waveReader.ReadData();
}
}
}
class WaveReader : IDisposable
{
FileInfo m_fInfo;
FileStream m_fStream;
BinaryReader m_binReader;
// RIFF chunk
byte[] chunkID;
UInt32 chunkSize;
byte[] format;
// fmt subchunk
byte[] fmtChunkID;
UInt32 fmtChunkSize;
UInt16 audioFormat;
UInt16 numChannels;
UInt32 sampleRate;
UInt32 byteRate;
UInt16 blockAssign;
UInt16 BitsPerSample;
// data subchunk
byte[] dataChunkID;
UInt32 dataChunkSize;
byte[] data8L; // 8-bit left channel
byte[] data8R; // 8-bit right channel
Int16[] data16L; // 16-bit left channel
Int16[] data16R; // 16-bit right channel
int numSamples;
public WaveReader()
{
}
public bool Open(String filename)
{
string str;
m_fInfo = new FileInfo(filename);
m_fStream = m_fInfo.OpenRead();
m_binReader = new BinaryReader(m_fStream);
chunkID = new byte[4];
format = new byte[4];
chunkID = m_binReader.ReadBytes(4);
chunkSize = m_binReader.ReadUInt32();
format = m_binReader.ReadBytes(4);
str = System.Text.ASCIIEncoding.ASCII.GetString(chunkID, 0, 4);
if (str != "RIFF")
return false;
str = System.Text.ASCIIEncoding.ASCII.GetString(format, 0, 4);
if (str != "WAVE")
return false;
//if (ReadFmt() == false)
// return false;
//if (ReadData() == false)
// return false;
return true;
}
public bool ReadFmt()
{
fmtChunkID = new byte[4];
fmtChunkID = m_binReader.ReadBytes(4);
string str = System.Text.ASCIIEncoding.ASCII.GetString(fmtChunkID, 0, 4);
if (str != "fmt ")
return false;
fmtChunkSize = m_binReader.ReadUInt32();
audioFormat = m_binReader.ReadUInt16();
numChannels = m_binReader.ReadUInt16();
sampleRate = m_binReader.ReadUInt32();
byteRate = m_binReader.ReadUInt32();
blockAssign = m_binReader.ReadUInt16();
BitsPerSample = m_binReader.ReadUInt16();
return true;
}
public void Dispose()
{
if (m_fStream != null)
m_fStream.Dispose();
}
}
}
Basically I have created a class WaveReader with your code and removed the internal call to ReadFmt. Then in the Main method I have checked the return code and in case of false I write to the console.
The problem likely exists in Main. The Open method closes the file at the end, and the ReadFmt call in Main reads from m_binReader. Either don't call ReadFmt from your main method after the Open call, or change Open to not close the file when it's done (and make the close explicit).
It looks like Open is doing all the work for you anyway (by calling ReadFmt and ReadData). You don't need to do it again in Main; just access the data from p.
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)
I am try to do some code using BinaryWriter and Then BinaryReader.
When I wanna write I use method Write().
But the problem is that between two lines of Write method there appears a new byte which is in ASCII table in decimal 31 (sometines 24).
You can see it on this image:
You can see that byte at index 4 (5th byte) is of ASCII decimal value 31. I didnt insert it there. As you can see 1st 4 bytes are reserved for a number (Int32), next are other data (some text mostly - this is not important now).
As you can see from the code i write:
- into 1st line a number 10
- into 2nd line text "This is some text..."
How come came that 5th byte (dec 31) in between??
And this is the code I have:
static void Main(string[] args)
{
//
//// SEND - RECEIVE:
//
SendingData();
Console.ReadLine();
}
private static void SendingData()
{
int[] commandNumbers = { 1, 5, 10 }; //10 is for the users (when they send some text)!
for (int i = 0; i < commandNumbers.Length; i++)
{
//convert to byte[]
byte[] allBytes;
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter bw = new BinaryWriter(ms))
{
bw.Write(commandNumbers[i]); //allocates 1st 4 bytes - FOR MAIN COMMANDS!
if (commandNumbers[i] == 10)
bw.Write("This is some text at command " + commandNumbers[i]); //HERE ON THIS LINE IS MY QUESTION!!!
}
allBytes = ms.ToArray();
}
//convert back:
int valueA = 0;
StringBuilder sb = new StringBuilder();
foreach (var b in GetData(allBytes).Select((a, b) => new { Value = a, Index = b }))
{
if (b.Index == 0) //1st num
valueA = BitConverter.ToInt32(b.Value, 0);
else //other text
{
foreach (byte _byte in b.Value)
sb.Append(Convert.ToChar(_byte));
}
}
if (sb.ToString().Length == 0)
sb.Append("ONLY COMMAND");
Console.WriteLine("Command = {0} and Text is \"{1}\".", valueA, sb.ToString());
}
}
private static IEnumerable<byte[]> GetData(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data))
{
using (BinaryReader br = new BinaryReader(ms))
{
int j = 0;
byte[] buffer = new byte[4];
for (int i = 0; i < data.Length; i++)
{
buffer[j++] = data[i];
if (i == 3) //SENDING COMMAND DATA
{
yield return buffer;
buffer = new byte[1];
j = 0;
}
else if (i > 3) //SENDING TEXT
{
yield return buffer;
j = 0;
}
}
}
}
}
If you look at the documentation for Write(string), you'll see that it writes a length-prefixed string. So the 31 is the number of characters in your string -- perfectly normal.
You should probably be using Encoding.GetBytes and then write the bytes instead of writing a string
for example
bw.Write(
Encoding.UTF8.GetBytes("This is some text at command " + commandNumbers[i])
);
When a string is written to a binary stream, the first thing it does is write the length of the string. The string "This is some text at command 10" has 31 characters, which is the value you're seeing.
You should check the documentation of methods you use before asking questions about them:
A length-prefixed string represents the string length by prefixing to
the string a single byte or word that contains the length of that
string. This method first writes the length of the string as a UTF-7
encoded unsigned integer, and then writes that many characters to the
stream by using the BinaryWriter instance's current encoding.
;-)
(Though in fact it is an LEB128 and not UTF-7, according to Wikipedia).
The reason this byte is there because you're adding a variable amount of information, so the length is needed. If you were to add two strings, where would you know where the first ended and the second began?
If you really don't want or need that length byte, you can always convert the string to a byte array and use that.
Ok, here is my edited code. I removed BinaryWriter (while BinaryReader is still there!!), and now it works very well - no more extra bytes.
What do you thing? Is there anytihng to do better, to make it run faster?
Expecially Im interesting for that foreach loop, which read from another method that is yield return type!!
New Code:
static void Main(string[] args)
{
//
//// SEND - RECEIVE:
//
SendingData();
Console.ReadLine();
}
private static void SendingData()
{
int[] commands = { 1, 2, 3 };
// 1 - user text
// 2 - new game
// 3 - join game
// ...
for (int i = 0; i < commands.Length; i++)
{
//convert to byte[]
byte[] allBytes;
using (MemoryStream ms = new MemoryStream())
{
// 1.st - write a command:
ms.Write(BitConverter.GetBytes(commands[i]), 0, 4);
// 2nd - write a text:
if (commands[i] == 1)
{
//some example text (like that user sends it):
string myText = "This is some text at command " + commands[i];
byte[] myBytes = Encoding.UTF8.GetBytes(myText);
ms.Write(myBytes, 0, myBytes.Length);
}
allBytes = ms.ToArray();
}
//convert back:
int valueA = 0;
StringBuilder sb = new StringBuilder();
foreach (var b in ReadingData(allBytes).Select((a, b) => new { Value = a, Index = b }))
{
if (b.Index == 0)
{
valueA = BitConverter.ToInt32(b.Value, 0);
}
else
{
sb.Append(Convert.ToChar(b.Value[0]));
}
}
if (sb.ToString().Length == 0)
sb.Append("ONLY COMMAND");
Console.WriteLine("Command = {0} and Text is \"{1}\".", valueA, sb.ToString());
}
}
private static IEnumerable<byte[]> ReadingData(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data))
{
using (BinaryReader br = new BinaryReader(ms))
{
int j = 0;
byte[] buffer = new byte[4];
for (int i = 0; i < data.Length; i++)
{
buffer[j++] = data[i];
if (i == 3) //SENDING COMMAND DATA
{
yield return buffer;
buffer = new byte[1];
j = 0;
}
else if (i > 3) //SENDING TEXT
{
yield return buffer;
j = 0;
}
}
}
}
}