2016-10-27 3 views
4

У меня есть функция, которую я использую для агрегирования потоков из zip-архива.Почтовый индекс в zip открывается недокументированному System.IO.Compression.SubReadStream

private void ExtractMiscellaneousFiles() 
{ 
    foreach (var miscellaneousFileName in _fileData.MiscellaneousFileNames) 
    { 
     var fileEntry = _archive.GetEntry(miscellaneousFileName); 
     if (fileEntry == null) 
     { 
      throw new ZipArchiveMissingFileException("Couldn't find " + miscellaneousFileName); 
     } 

     var stream = fileEntry.Open(); 
     OtherFileStreams.Add(miscellaneousFileName, (DeflateStream) stream); 
    } 
} 

В большинстве случаев это хорошо работает. Тем не менее, если у меня есть почтовый индекс в пределах почтового индекса, я получаю excpetion на отливку потока в DeflateStream:

System.InvalidCastException: Невозможно привести объект типа «System.IO.Compression.SubReadStream» к типу 'System.IO.Compression.DeflateStream.

Я не могу найти документацию Microsoft для SubReadStream. Я хотел бы, чтобы мой почтовый индекс находился в zip как DeflateStream. Это возможно? Если да, то как?

UPDATE

Еще нет успеха. Я попытался @ предложение Sunshine вы копирования потока, используя следующий код:

private void ExtractMiscellaneousFiles() 
{ 
    _logger.Log("Extracting misc files..."); 

    foreach (var miscellaneousFileName in _fileData.MiscellaneousFileNames) 
    { 
     _logger.Log($"Opening misc file stream for {miscellaneousFileName}"); 

     var fileEntry = _archive.GetEntry(miscellaneousFileName); 
     if (fileEntry == null) 
     { 
      throw new ZipArchiveMissingFileException("Couldn't find " + miscellaneousFileName); 
     } 

     var openStream = fileEntry.Open(); 
     var deflateStream = openStream; 
     if (!(deflateStream is DeflateStream)) 
     { 
      var memoryStream = new MemoryStream(); 
      deflateStream.CopyTo(memoryStream); 
      memoryStream.Position = 0; 
      deflateStream = new DeflateStream(memoryStream, CompressionLevel.NoCompression, true); 
     } 
     OtherFileStreams.Add(miscellaneousFileName, (DeflateStream)deflateStream); 
    } 
} 

Но я получаю

System.NotSupportedException: Поток не поддерживает чтение.

Я осмотрел deflateStream.CanRead и это правда.

Я обнаружил, что это происходит не только на почтовых индексах, но и на файлах, находящихся в zip-файле, но не сжатых (например, слишком малых). Конечно, есть способ справиться с этим; наверняка кто-то сталкивался с этим раньше. Я открываю щедрость по этому вопросу.

Вот .NET source для SubReadStream, благодаря @Quantic.

+0

Что такое '_archive'? – Quantic

+0

'System.IO.Compression.ZipArchive'. Благодарю. –

+0

Должен быть .NET Core: https://github.com/dotnet/corefx/blob/bffef76f6af208e2042a2f27bc081ee908bb390b/src/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs – Quantic

ответ

3

Тип возврата ZipArchiveEntry.Open() - Stream. Абстрактный тип, на практике это может быть DeflateStream (вы были бы счастливы), SubReadStream (boo) или WrappedStream (boo). Горе вам, если они однажды решат улучшить класс и используют ZopfliStream (boo). Обходной путь не очень хорош, вы пытаетесь сдуть данные, которые не сжаты (boo).

Слишком много боос.

Только good Решение должно изменить тип вашего члена OtherFileStreams. Мы не можем видеть это, пахнет List<DeflateStream>. Это должно быть List<Stream>.

+0

Решается с помощью 'MemoryStream' вместо' DeflateStream'. Спасибо за то, что вы решились на более высоком уровне. –

1

Таким образом, при хранении zip-файла в другом почтовом индексе он не сбрасывает почтовый индекс, а просто вставляет содержимое zip вместе с остальными файлами с некоторой информацией о том, что эти записи являются частью подкаталога zip-файл. Это имеет смысл, потому что применение сжатия к тому, что уже сжато, является пустой тратой времени.

Этот zip-файл отмечен как CompressionMethodValues.Stored в архиве, что заставляет .NET просто вернуть исходный поток, который он прочитал, вместо этого, чтобы обернуть его в DeflateStream.

Источник здесь: https://github.com/dotnet/corefx/blob/master/src/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs#L670

Вы могли бы передать поток в ZipArchive, если это не DeflateStream (если вы заинтересованы в файле внутри)

var stream = entry.Open(); 
if (!(stream is DeflateStream)) 
{ 
    var subArchive = new ZipArchive(stream); 
} 

Или вы можете скопировать поток в FileStream (если вы хотите сохранить его на диск)

var stream = entry.Open(); 
if (!(stream is DeflateStream)) 
{ 
    var fs = File.Create(Path.GetTempFileName()); 
    stream.CopyTo(fs); 
    fs.Close(); 
} 

Или скопируйте его в любой поток, который вы заинтересованы в использовании.

Примечание: Это также как .NET 4.6 ведет себя

Смежные вопросы