如果我不知道它的大小,我需要类似于 ReadToEnd 或 ReadAllBytes 的东西来使用 MappedViewAccessor 读取 MemoryMappedFile 的所有内容,我该怎么做?
我已经搜索过了,我看到了这个问题,但这不是我要找的东西:
编辑:
有一个问题, (int)stream.Length 没有给我正确的长度,而是给出了使用的内部缓冲区的大小!我需要刷新这个问题,因为它非常紧迫。
如果我不知道它的大小,我需要类似于 ReadToEnd 或 ReadAllBytes 的东西来使用 MappedViewAccessor 读取 MemoryMappedFile 的所有内容,我该怎么做?
我已经搜索过了,我看到了这个问题,但这不是我要找的东西:
编辑:
有一个问题, (int)stream.Length 没有给我正确的长度,而是给出了使用的内部缓冲区的大小!我需要刷新这个问题,因为它非常紧迫。
而是使用流:
public static Byte[] ReadMMFAllBytes(string fileName)
{
using (var mmf = MemoryMappedFile.OpenExisting(fileName))
{
using (var stream = mmf.CreateViewStream())
{
using (BinaryReader binReader = new BinaryReader(stream))
{
return binReader.ReadBytes((int)stream.Length);
}
}
}
}
这很难回答,因为您的应用程序仍有许多细节尚未指定,但我认为 Guffa 和 Amer 的答案仍然部分正确:
如果以上所有内容都适合您的应用程序,那么以下内容应该有效:
static byte[] ReadMemoryMappedFile(string fileName)
{
long length = new FileInfo(fileName).Length;
using (var stream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite))
{
using (var mmf = MemoryMappedFile.CreateFromFile(stream, null, length, MemoryMappedFileAccess.Read, null, HandleInheritability.Inheritable, false))
{
using (var viewStream = mmf.CreateViewStream(0, length, MemoryMappedFileAccess.Read))
{
using (BinaryReader binReader = new BinaryReader(viewStream))
{
var result = binReader.ReadBytes((int)length);
return result;
}
}
}
}
}
要写入数据,您可以使用以下命令:
private static void WriteData(string fileName, byte[] data)
{
using (var stream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
{
using (var mmf = MemoryMappedFile.CreateFromFile(stream, null, data.Length, MemoryMappedFileAccess.ReadWrite, null, HandleInheritability.Inheritable, true))
{
using (var view = mmf.CreateViewAccessor())
{
view.WriteArray(0, data, 0, data.Length);
}
}
stream.SetLength(data.Length); // Make sure the file is the correct length, in case the data got smaller.
}
}
但是,当您执行上述所有操作时,您可能会直接使用该文件并避免内存映射。如果将其映射到文件系统是不可接受的,那么 Guffa 在数据本身中编码长度(或结束标记)的答案可能是最好的。
你不能那样做。
视图访问器是使用最小系统页面大小创建的,这意味着它可能大于实际文件。视图流只是访问器的流形式,因此它也会给出相同的行为。
“视图以系统页面为单位提供,视图大小向上舍入到下一个系统页面大小”
http://msdn.microsoft.com/en-us/library/dd267577.aspx
访问器很乐意在实际文件之外进行读写,而不会引发异常。读取时,文件外的任何字节都将为零。写入时,文件外写入的字节将被忽略。
要从具有原始文件确切大小的内存映射文件中读取文件,您必须已经知道该大小。
MemoryMappedFile 创建的流的长度与文件系统页面大小(通常为 4096)对齐。您必须从其他地方获取文件大小。如果它是内存映射文件,您可以使用该代码:
byte[] ReadAllMemoryMappedFileBytes(string filePath)
{
var fileInfo = new FileInfo(filePath);
using (var file = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open))
using (var stream = file.CreateViewAccessor())
{
byte[] bytes = new byte[fileInfo.Length];
stream.ReadArray(0, bytes, 0, bytes.Length);
return bytes;
}
}
使用 FileInfo 类获取长度,如下所示
using System.Data;
using System.IO;
using System.IO.Compression;
using System.IO.MemoryMappedFiles;
// ...
public void WriteToMemoryMap(DataSet ds, string key, string fileName)
{
var bytes = CompressData(ds);
using (MemoryMappedFile objMf = MemoryMappedFile.CreateFromFile(fileName, FileMode.OpenOrCreate, key, bytes.Length))
{
using (MemoryMappedViewAccessor accessor = objMf.CreateViewAccessor())
{
accessor.WriteArray(0, bytes, 0, bytes.Length);
}
}
}
public DataSet ReadFromMemoryMap(string fileName)
{
var fi = new FileInfo(fileName);
var length = (int)fi.Length;
var newBytes = new byte[length];
using (MemoryMappedFile objMf = MemoryMappedFile.CreateFromFile(fileName, FileMode.Open))
{
using (MemoryMappedViewAccessor accessor = objMf.CreateViewAccessor())
{
accessor.ReadArray(0, newBytes, 0, length);
}
}
return DecompressData(newBytes);
}
public byte[] CompressData(DataSet ds)
{
try
{
byte[] data = null;
var memStream = new MemoryStream();
var zipStream = new GZipStream(memStream, CompressionMode.Compress);
ds.WriteXml(zipStream, XmlWriteMode.WriteSchema);
zipStream.Close();
data = memStream.ToArray();
memStream.Close();
return data;
}
catch (Exception)
{
return null;
}
}
public DataSet DecompressData(byte[] data)
{
try
{
var memStream = new MemoryStream(data);
var unzipStream = new GZipStream(memStream, CompressionMode.Decompress);
var objDataSet = new DataSet();
objDataSet.ReadXml(unzipStream, XmlReadMode.ReadSchema);
unzipStream.Close();
memStream.Close();
return objDataSet;
}
catch (Exception)
{
return null;
}
}
只是将@Amer Sawan 解决方案翻译为 Vb.NET:
' Usage Example:
' Dim ReadBytes As Byte() = ReadMemoryMappedFile(Name:="My MemoryMappedFile Name") ' Read the byte-sequence from memory.
' Dim Message As String = System.Text.Encoding.ASCII.GetString(ReadBytes.ToArray) ' Convert the bytes to String.
' Message = Message.Trim({ControlChars.NullChar}) ' Remove null chars (leading zero-bytes)
' MessageBox.Show(Message, "", MessageBoxButtons.OK) ' Show the message. '
'
''' <summary>
''' Reads a byte-sequence from a <see cref="IO.MemoryMappedFiles.MemoryMappedFile"/> without knowing the exact size.
''' Note that the returned byte-length is rounded up to 4kb,
''' this means if the mapped memory-file was written with 1 byte-length, this method will return 4096 byte-length.
''' </summary>
''' <param name="Name">Indicates an existing <see cref="IO.MemoryMappedFiles.MemoryMappedFile"/> assigned name.</param>
''' <returns>System.Byte().</returns>
Private Function ReadMemoryMappedFile(ByVal Name As String) As Byte()
Try
Using MemoryFile As IO.MemoryMappedFiles.MemoryMappedFile =
IO.MemoryMappedFiles.MemoryMappedFile.OpenExisting(Name, IO.MemoryMappedFiles.MemoryMappedFileRights.ReadWrite)
Using Stream = MemoryFile.CreateViewStream()
Using Reader As New BinaryReader(Stream)
Return Reader.ReadBytes(CInt(Stream.Length))
End Using ' Reader
End Using ' Stream
End Using ' MemoryFile
Catch exNoFile As IO.FileNotFoundException
Throw
Return Nothing
Catch ex As Exception
Throw
End Try
End Function
我想从 MemoryStream .ToArray() 方法中得到一些东西来返回所有字节,下面的代码对我有用:
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(MemoryMappedName))
{
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
using (MemoryStream memStream = new MemoryStream())
{
stream.CopyTo(memStream);
return memStream.ToArray();
}
}
}
干杯!