Як скопіювати файл у C #, використовуючи сторонні API?


175

Я впевнений, що це не дублікат, тож потерпіть зі мною всього хвилину.

Як я можу програмно (C #) ZIP-файл (у Windows), не використовуючи сторонніх бібліотек? Мені потрібен вихідний дзвінок з Windows або щось подібне; Мені дуже не подобається ідея розпочати процес, але я стану, якщо мені це доведеться. Дзвінок PInovke був би набагато кращим.

Якщо цього не зробити, дозвольте мені розповісти, що я насправді намагаюся досягти: мені потрібна можливість дозволити користувачу завантажувати колекцію документів за один запит. Будь-які ідеї, як це досягти?



1
@Chesso: Так, зі сторінки ASPX.
Естебан Арая

1
Цей приклад я знайшов корисним, коли шукав те саме, що кілька тижнів тому: syntaxwarriors.com/2012/…
JensB

2
Якщо використовується 4.5 Framework, то тепер існують класи ZipArchive та ZipFile.
GalacticJello

Хто-небудь використовував DotNetZip ??
Гарячий лизає

Відповіді:


85

Ви використовуєте .NET 3.5? Ви можете використовувати ZipPackageклас та споріднені класи. Це більше, ніж просто стискання списку файлів, оскільки він хоче тип MIME для кожного доданого вами файлу. Це може робити те, що ви хочете.

Зараз я використовую ці класи для подібної проблеми для архівації декількох пов'язаних файлів в один файл для завантаження. Ми використовуємо розширення для того, щоб пов’язати завантажений файл з нашим настільним додатком. Одна невелика проблема, з якою ми зіткнулися, полягала в тому, що неможливо просто використовувати сторонній інструмент, наприклад 7-zip, для створення zip-файлів, оскільки код клієнтської сторони не може його відкрити - ZipPackage додає прихований файл, що описує тип вмісту кожен компонентний файл і не може відкрити zip-файл, якщо відсутній файл типу вмісту.


1
О так, як я тебе люблю! Дякую Брайану; Юо просто врятував нам багато головних болів і трохи $$$.
Естебан Арая

6
Зауважте, що це не завжди працює в зворотному напрямку. Деякі файли Zip не будуть регідратовані за допомогою класу ZipPackage. Файли, зроблені за допомогою ZipPackage, ви повинні бути хорошими.
Крейг

Зауважте, що ZipPackage не може додавати до існуючого пакету-блискавки.
ΩmegaMan

Зітхання: "Тип або простір імен" Packaging "не існує в просторі імен" System.IO ".
Hot Licks

2
(Відповідь на вищезгадане "зітхання": відкрийте "Посилання" та додайте (досить нелогічно) "WindowsBase".)
Hot Licks

307

Як я можу програмно (C #) ZIP-файл (у Windows), не використовуючи сторонніх бібліотек?

Якщо ви використовували Рамку 4.5+ , то тепер існують класи ZipArchive та ZipFile .

using (ZipArchive zip = ZipFile.Open("test.zip", ZipArchiveMode.Create))
{
    zip.CreateEntryFromFile(@"c:\something.txt", "data/path/something.txt");
}

Вам потрібно додати посилання на:

  • System.IO.Compression
  • System.IO.Compression.FileSystem

Для націлювання на .NET Core net46 вам потрібно додати залежності для

  • System.IO.Compression
  • System.IO.Compression.ZipFile

Приклад project.json:

"dependencies": {
  "System.IO.Compression": "4.1.0",
  "System.IO.Compression.ZipFile": "4.0.1"
},

"frameworks": {
  "net46": {}
}

Для .NET Core 2.0 лише необхідне додавання простого використання оператора:

  • використання System.IO.Compression;

4
Як це не отримало більше відгуків? Це єдина пряма відповідь.
Метт Кашатт

12
Тому що питання п’ять років, тоді як на цю відповідь лише два місяці. Дерп :-P
Геліак

3
@heliac все ще Stackoverflow thingie повинен бути сховищем для запитань і відповідей, і в дусі найкраща відповідь повинна бути зверху ... (чорт, я знав, що це не працює)
Offler

5
На всякий випадок, коли це комусь допомагає, другим аргументом є запис файлу. Це шлях, яким буде вилучено файл відносно папки, що розпаковує. У Windows 7 я виявив, що якщо запис у файл - це повний шлях, наприклад, @ "D: \ Temp \ file1.pdf", то власний екстрактор Windows не працює. Ви можете зіткнутися з цією проблемою, якщо ви просто використовуєте імена файлів, отримані в каталозі Directory.GetFiles (). Найкраще витягти ім’я файлу, використовуючи Path.GetFileName () для аргументу введення файлу.
Маніш

2
Я не можу знайти це в 4.5.2?
користувач3791372

11

Я опинився в тій самій ситуації, хотів .NET замість сторонньої бібліотеки. Як ще один плакат, згаданий вище, просто використання класу ZipPackage (введеного в .NET 3.5) недостатньо. Є додатковий файл, який ОБОВ'ЯЗКОВО бути включений до архіву, щоб ZipPackage працював. Якщо цей файл доданий, отриманий пакет ZIP можна відкрити безпосередньо з Провідника Windows - не проблема.

Все, що вам потрібно зробити, - це додати файл [Content_Types] .xml до кореня архіву з вузлом «За замовчуванням» для кожного розширення, яке ви хочете включити. Після додавання я міг переглядати пакунок із Провідника Windows або програматично розпаковувати та читати його вміст.

Більше інформації про файл [Content_Types] .xml можна знайти тут: http://msdn.microsoft.com/en-us/magazine/cc163372.aspx

Ось зразок файлу [Content_Types] .xml (повинен бути названий точно):

<?xml version="1.0" encoding="utf-8" ?>
<Types xmlns=
    "http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="xml" ContentType="text/xml" /> 
  <Default Extension="htm" ContentType="text/html" /> 
  <Default Extension="html" ContentType="text/html" /> 
  <Default Extension="rels" ContentType=
    "application/vnd.openxmlformats-package.relationships+xml" /> 
  <Default Extension="jpg" ContentType="image/jpeg" /> 
  <Default Extension="png" ContentType="image/png" /> 
  <Default Extension="css" ContentType="text/css" /> 
</Types>

І C # для створення ZIP-файлу:

var zipFilePath = "c:\\myfile.zip"; 
var tempFolderPath = "c:\\unzipped"; 

    using (Package package = ZipPackage.Open(zipFilePath, FileMode.Open, FileAccess.Read)) 
    { 
        foreach (PackagePart part in package.GetParts()) 
        { 
            var target = Path.GetFullPath(Path.Combine(tempFolderPath, part.Uri.OriginalString.TrimStart('/'))); 
            var targetDir = target.Remove(target.LastIndexOf('\\')); 

            if (!Directory.Exists(targetDir)) 
                Directory.CreateDirectory(targetDir); 

            using (Stream source = part.GetStream(FileMode.Open, FileAccess.Read)) 
            { 
                source.CopyTo(File.OpenWrite(target)); 
            } 
        } 
    } 

Примітка:


12
Гарний зразок, але він не створює ZIP-файл. Він розпаковує наявний файл.
Метт Варблов

9
    private static string CompressFile(string sourceFileName)
    {
        using (ZipArchive archive = ZipFile.Open(Path.ChangeExtension(sourceFileName, ".zip"), ZipArchiveMode.Create))
        {
            archive.CreateEntryFromFile(sourceFileName, Path.GetFileName(sourceFileName));
        }
        return Path.ChangeExtension(sourceFileName, ".zip");
    }

Як я можу отримати sourceFileName, коли я знаходжусь у веб-сайтах, отримуючи HttpContext.Current.Request?
Олівертех

Стиснути ще один файл?
Кікенет

1

Виходячи з відповіді Саймона Макензі на це запитання , я б запропонував використовувати пару таких методів:

    public static void ZipFolder(string sourceFolder, string zipFile)
    {
        if (!System.IO.Directory.Exists(sourceFolder))
            throw new ArgumentException("sourceDirectory");

        byte[] zipHeader = new byte[] { 80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

        using (System.IO.FileStream fs = System.IO.File.Create(zipFile))
        {
            fs.Write(zipHeader, 0, zipHeader.Length);
        }

        dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
        dynamic source = shellApplication.NameSpace(sourceFolder);
        dynamic destination = shellApplication.NameSpace(zipFile);

        destination.CopyHere(source.Items(), 20);
    }

    public static void UnzipFile(string zipFile, string targetFolder)
    {
        if (!System.IO.Directory.Exists(targetFolder))
            System.IO.Directory.CreateDirectory(targetFolder);

        dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
        dynamic compressedFolderContents = shellApplication.NameSpace(zipFile).Items;
        dynamic destinationFolder = shellApplication.NameSpace(targetFolder);

        destinationFolder.CopyHere(compressedFolderContents);
    }
}

0

Схоже, Windows може просто дозволити вам це зробити ...

На жаль, я не думаю, що ви збираєтеся обійтися окремим процесом, якщо не перейдете до стороннього компонента.


0

Додайте ці 4 функції до свого проекту:

        public const long BUFFER_SIZE = 4096;
    public static void AddFileToZip(string zipFilename, string fileToAdd)
    {
        using (Package zip = global::System.IO.Packaging.Package.Open(zipFilename, FileMode.OpenOrCreate))
        {
            string destFilename = ".\\" + Path.GetFileName(fileToAdd);
            Uri uri = PackUriHelper.CreatePartUri(new Uri(destFilename, UriKind.Relative));
            if (zip.PartExists(uri))
            {
                zip.DeletePart(uri);
            }
            PackagePart part = zip.CreatePart(uri, "", CompressionOption.Normal);
            using (FileStream fileStream = new FileStream(fileToAdd, FileMode.Open, FileAccess.Read))
            {
                using (Stream dest = part.GetStream())
                {
                    CopyStream(fileStream, dest);
                }
            }
        }
    }
    public static void CopyStream(global::System.IO.FileStream inputStream, global::System.IO.Stream outputStream)
    {
        long bufferSize = inputStream.Length < BUFFER_SIZE ? inputStream.Length : BUFFER_SIZE;
        byte[] buffer = new byte[bufferSize];
        int bytesRead = 0;
        long bytesWritten = 0;
        while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) != 0)
        {
            outputStream.Write(buffer, 0, bytesRead);
            bytesWritten += bytesRead;
        }
    }
    public static void RemoveFileFromZip(string zipFilename, string fileToRemove)
    {
        using (Package zip = global::System.IO.Packaging.Package.Open(zipFilename, FileMode.OpenOrCreate))
        {
            string destFilename = ".\\" + fileToRemove;
            Uri uri = PackUriHelper.CreatePartUri(new Uri(destFilename, UriKind.Relative));
            if (zip.PartExists(uri))
            {
                zip.DeletePart(uri);
            }
        }
    }
    public static void Remove_Content_Types_FromZip(string zipFileName)
    {
        string contents;
        using (ZipFile zipFile = new ZipFile(File.Open(zipFileName, FileMode.Open)))
        {
            /*
            ZipEntry startPartEntry = zipFile.GetEntry("[Content_Types].xml");
            using (StreamReader reader = new StreamReader(zipFile.GetInputStream(startPartEntry)))
            {
                contents = reader.ReadToEnd();
            }
            XElement contentTypes = XElement.Parse(contents);
            XNamespace xs = contentTypes.GetDefaultNamespace();
            XElement newDefExt = new XElement(xs + "Default", new XAttribute("Extension", "sab"), new XAttribute("ContentType", @"application/binary; modeler=Acis; version=18.0.2application/binary; modeler=Acis; version=18.0.2"));
            contentTypes.Add(newDefExt);
            contentTypes.Save("[Content_Types].xml");
            zipFile.BeginUpdate();
            zipFile.Add("[Content_Types].xml");
            zipFile.CommitUpdate();
            File.Delete("[Content_Types].xml");
            */
            zipFile.BeginUpdate();
            try
            {
                zipFile.Delete("[Content_Types].xml");
                zipFile.CommitUpdate();
            }
            catch{}
        }
    }

І використовуйте їх так:

foreach (string f in UnitZipList)
{
    AddFileToZip(zipFile, f);
    System.IO.File.Delete(f);
}
Remove_Content_Types_FromZip(zipFile);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.