2017-09-21 2 views
2

J'ai un serveur hébergeant une grande archive de fichiers XML et une API récupère les fichiers demandés dans un zip. Si je sélectionne environ 11 fichiers ou moins, le zip revient très bien. Si je récupère plus, je reçois l'erreur suivante en essayant d'ouvrir le zip: «Windows ne peut pas ouvrir le dossier Le dossier compressé (zip) est non valide. »Le téléchargement de fichiers en tant que résultat zip dans les zips endommagés dans ASP.NET MVC

Voici les classes de données et méthodes pour créer le zip:

//Archive file containing filename and content as memory stream 
public class ArchiveFile { 
    public string FileName; 
    public System.IO.MemoryStream FileContent; 
} 

//Method to retrieve archive files and zip them 
public static System.IO.MemoryStream GetFilesAsZip (string[] arrFileNames) { 
    MemoryStream zipStream = null; 
    using (zipStream = new MemoryStream()) { 
     // Retrieve files using above method 
     ArchiveFile[] retrievedFiles = GetFilesFromArchive(arrFileNames); 
     // Initialize new ZipArchive on the return object's MemoryStream property 
     using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Update, leaveOpen: true)) { 
      // Write file entries into archive 
      foreach (ArchiveFile dataFile in retrievedFiles) { 
       if (dataFile.FileContent != null) { 
        // Create new ZipArchiveEntry with content 
        ZipArchiveEntry zipEntry = archive.CreateEntry(dataFile.FileName); 
        dataFile.FileContent.WriteTo(zipEntry.Open()); 
       }//end if 
      } // end foreach 
     } //end using 
    } //end using 
    return zipStream; 
}//end method 

//API to return content to user as an MVC File Content Result 
[HttpGet] 
public ActionResult DownloadFiles (string [] fileNames) { 
    FileContentResult data = new FileContentResult(GetFiles(fileNames).GetBuffer(), “application/zip”) { FileDownloadName = “files.zip” }; 
    return data; 
} //end method 

La corruption peut avoir quelque chose à voir avec l'allocation d'espace lors de l'écriture au flux mémoire. J'ai remarqué que tous mes zips "réussis" (11 fichiers ou moins) étaient de taille 259 Ko mais tous les zips "ratés" (plus de 11 fichiers) étaient de taille 517 Ko, avec quelques zips plus gros de taille 1034 Ko. Il me semble que c'est une coïncidence si ce sont tous des multiples de 258,5 Ko, d'autant plus qu'un fichier zip de 11 fichiers donne un fichier zip de 259 Ko mais un zip de 12 fichiers donne un zip de 517 Ko.

Un aperçu de ce que cela pourrait être?

+0

Pouvez-vous partager les fichiers XML en quelque sorte? – Isma

+0

Malheureusement, je ne peux pas, ils contiennent des informations privées. – crodriguez

+0

vous n'utilisez pas correctement MemoryStream. Au lieu de renouveler MemoryStream en dehors de l'utilisation, créez un tableau d'octets et renvoyez-le. son zipStream.ToArray() <- sauvegarder ce tableau en octets que vous devriez retourner au lieu du MemoryStream dont vous ne disposez jamais correctement –

Répondre

0

J'ai eu du succès dans le passé en faisant return File(fileLocation, "application/zip", fileName); où fileLocation est le chemin d'accès au dossier et nomFichier est le nom du dossier réel. Vous pouvez le faire correctement dans votre ActionResult.

+0

oui j'ai mis ensemble un exemple complet pour une autre question –

+0

J'ai bien peur que cela ne marche pas pour moi. Les zips ne sont pas stockés sur le disque car ils sont générés dynamiquement à partir de la combinaison de fichiers demandée. – crodriguez

1

ASP.Net Core API Controller returns corrupted excel file

retour dans votre contrôleur

return new FileResult("demo.zip", Path.Combine(sWebRootFolder, sFileName), "application/zip"); 

ajouter ce code

public class FileResult : ActionResult 
    { 
     public FileResult(string fileDownloadName, string filePath, string contentType) 
     { 
      FileDownloadName = fileDownloadName; 
      FilePath = filePath; 
      ContentType = contentType; 
     } 

     public string ContentType { get; private set; } 
     public string FileDownloadName { get; private set; } 
     public string FilePath { get; private set; } 

     public async override Task ExecuteResultAsync(ActionContext context) 
     { 
      var response = context.HttpContext.Response; 
      response.ContentType = ContentType; 
      context.HttpContext.Response.Headers.Add("Content-Disposition", new[] { "attachment; filename=" + FileDownloadName }); 
      using (var fileStream = new FileStream(FilePath, FileMode.Open)) 
      { 
       await fileStream.CopyToAsync(context.HttpContext.Response.Body); 
      } 
     } 
    } 

votre code refactorisé

public byte[] GetFilesAsZip (string[] arrFileNames) { 
    byte[] buffer = null; 
    using (MemoryStream zipStream = new MemoryStream()) { 
     // Retrieve files using above method 
     ArchiveFile[] retrievedFiles = GetFilesFromArchive(arrFileNames); 
     // Initialize new ZipArchive on the return object's MemoryStream property 
     using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Update, leaveOpen: true)) { 
      // Write file entries into archive 
      foreach (ArchiveFile dataFile in retrievedFiles) { 
       if (dataFile.FileContent != null) { 
        // Create new ZipArchiveEntry with content 
        ZipArchiveEntry zipEntry = archive.CreateEntry(dataFile.FileName); 
        dataFile.FileContent.WriteTo(zipEntry.Open()); 
       }//end if 
      } // end foreach 
     } //end using 
     buffer = zipStream.ToArray(); 
    } //end using 
    return buffer; 
}//end method 

vous devriez être en mesure de ch ange this to

FileContentResult data = new FileContentResult(GetFiles(fileNames), “application/zip”) { FileDownloadName = “files.zip” };