Поток, который наследует класс Stream и может быть сериализован C # - PullRequest
2 голосов
/ 09 сентября 2011

У меня есть приложение ASP.NET MVC (я также использую jQuery).Я разрешаю пользователю загружать файл, используя HttpPostedFileBase класс.Затем я использую свойство InputStream типа Stream, чтобы сохранить поток файлов в моей базе данных, где я сначала сериализую свой объект.Stream можно разделить, поэтому здесь нет проблем.

Проблема начинается, когда пользователь не загружает файл, и я хочу в этом случае использовать другой файл по умолчанию, который у меня есть где-то.В этом случае все должно быть аналогично первому, поэтому в моей базе данных будет Stream.Поэтому я должен создать экземпляр Stream и сохранить его.Stream абстрактно, поэтому я не могу создать экземпляр Stream.Вместо этого я использовал FileStream, который наследует Stream.Проблема в том, что FileStream не является seralizeable, поэтому здесь у меня есть проблема.

Как я могу ее решить?Есть ли другой поток, который я могу использовать, который наследует Stream и сериализуем?

Ответы [ 2 ]

4 голосов
/ 09 сентября 2011

Не сериализовать поток для хранения; поток - это «шланг», а не «ведро». Вместо этого прочитайте поток и сохраните двоичные данные (большинство баз данных имеют тип данных для двоичных данных, например varbinary(max)). Если это часть объектной модели, я был бы склонен иметь свойство byte[] (со значимым именем); это будет сериализовать тривиально как часть модели. Просто прочитайте поток, чтобы создать byte[]; Работа выполнена. Например:

public static byte[] ReadToEnd(this Stream s) {
    using(var ms = new MemoryStream()) {
        s.CopyTo(ms);
        return ms.ToArray();
    }
}
0 голосов
/ 09 сентября 2011

Оба Джон и Марк имеют хорошие ответы.Я предпочитаю Марк, так как у вас есть возможность последовательного чтения слоя SQL из потока вместо буферизации всех данных в памяти (что может привести к OutOfMemoryException).

Однако на базовом уровневы подходите к этому неправильно.Механизмам SQL вообще не нравится работать с большими значениями столбцов - они невероятно неэффективны.Есть еще одна «база данных», которую вы можете использовать - ваша файловая система.

Поэтому, как правило, вы определяете структуру вашей БД следующим образом:

CREATE TABLE [dbo].[Files]
(
   [ID] INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
   [Name] NVARCHAR(255) NOT NULL,
   [Storage] UNIQUEIDENTIFIER NOT NULL
);

В земле C # вы сначала пишете файлна диск и используйте этот идентификатор для обновления базы данных:

/// <summary>
/// Writes a stream to a file and returns a <see cref="Guid"/> that
/// can be used to retrieve it again.
/// </summary>
/// <param name="incomingFile">The incoming file.</param>
/// <returns>The <see cref="Guid"/> that should be used to identify the file.</returns>
public static Guid WriteFile(Stream incomingFile)
{
    var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MyApplication");
    path = Path.Combine(path, "BinaryData");

    var guid = Guid.NewGuid();
    var ba = guid.ToByteArray();

    // Create the path for the GUID.
    path = Path.Combine(ba[0].ToString("x2"));
    path = Path.Combine(ba[1].ToString("x2"));
    path = Path.Combine(ba[2].ToString("x2"));
    Directory.CreateDirectory(path); // Always succeeds, even if the directory already exists.

    path = Path.Combine(guid.ToString() + ".dat");
    using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
    {
        var buffer = new byte[Environment.SystemPageSize];
        var length = 0;
        while ((length = incomingFile.Read(buffer, 0, buffer.Length)) != 0)
            fs.Write(buffer, 0, buffer.Length);
    }

    return guid;
}

/// <summary>
/// Deletes a file created by <see cref="WriteFile"/>.
/// </summary>
/// <param name="guid">The original <see cref="Guid"/> that was returned by <see cref="WriteFile"/>.</param>
public static void DeleteFile(Guid guid)
{
    var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MyApplication");
    path = Path.Combine(path, "BinaryData");

    var ba = guid.ToByteArray();

    // Create the path for the GUID.
    path = Path.Combine(ba[0].ToString("x2"));
    path = Path.Combine(ba[1].ToString("x2"));
    path = Path.Combine(ba[2].ToString("x2"));
    path = Path.Combine(guid.ToString() + ".dat");
    if (File.Exists(path))
        File.Delete(path);
}

/// <summary>
/// Reads the a file that was created by <see cref="WriteFile"/>.
/// </summary>
/// <param name="guid">The original <see cref="Guid"/> that was returned by <see cref="WriteFile"/>.</param>
/// <returns>The stream that can be used to read the file.</returns>
public static Stream ReadFile(Guid guid)
{
    var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MyApplication");
    path = Path.Combine(path, "BinaryData");

    var ba = guid.ToByteArray();

    // Create the path for the GUID.
    path = Path.Combine(ba[0].ToString("x2"));
    path = Path.Combine(ba[1].ToString("x2"));
    path = Path.Combine(ba[2].ToString("x2"));
    path = Path.Combine(guid.ToString() + ".dat");
    return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
}

Вам также следует изучить Транзакционная NTFS , чтобы обеспечить синхронизацию вашей БД и файловой системы.Неэффективность хранения BLOB в MsSQL является одной из причин, по которой Microsoft внедрила TxF - поэтому следуйте их советам - не храните BLOB / файлы в SQL .

Примечание: Наличие вложенных папок (ba[0 through 2]) важно как для производительности, так и для ограничений файловой системы - одна папка не может содержать действительно большое количество файлов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...