Asked by:
how to download multiple folders in one zip file through Web API

Question
-
User2097725413 posted
Hi
I am working on Web API, I have a requirement where the user wants to download multiple folders and its contents into one folder and able to download in one zip file.
for.e.g.
Multiple folders are
Lesson1
Lesson2
Lesson3
.............
Lesson100
A user wants to download selected lessons, i.e. Lesson1, Lesson2, Lesson5, Lesson7.
when the user downloads all the lessons it should download in one zip file, i.e. in a folder "UserLessons" it will add a user-selected folder and its contents and will download the folder "UserLessons" in one zip file.
The above scenario I want to implement dynamically in Web API.
Please suggest the approach to implement, if possible share me the sample of code.
Thanks,
Uday
Thursday, August 27, 2020 4:29 AM
All replies
-
User1686398519 posted
Hi Uday Mahajan,
According to your needs, I made a detailed example, you can refer to it, I hope it can help you.
- This is an MVC project and uses EF, you can modify it according to your own actual situation.
- You only need to write the relevant code for downloading ZIP format files in WebAPI.
- In order to display related files on the view,
jQuery jsTree is used in the example.
- You can install jquery.jstree through NuGet.
- You need to reference the style.min.css file and the jstree.min.js file.
- In order to download files in ZIP format, you can choose
SharpZipLib.
- You need to install SharpZipLib via NuGet.
Model
public class TreeViewNode { public string id { get; set; } public string parent { get; set; } public string text { get; set; } } public class File { public int FileId { get; set; } public string FileName { get; set; } public string FilePath { get; set; } public int LessonId { get; set; } [ForeignKey("LessonId")] public Lesson Lesson { get; set; } } public class Lesson { public int LessonId { get; set; } public string LessonName { get; set; } public ICollection<File> Files { get; set; } }
FileDemoContext
public class FileDemoContext : DbContext { public FileDemoContext() : base("name=FileDemoContext") { } public virtual DbSet<File> Files { get; set; } public virtual DbSet<Lesson> Lessons { get; set; } }
Controller
public class HomeController : Controller { public FileDemoContext db = new FileDemoContext(); public ActionResult Index() { ViewBag.Json = GetAllFiles();//set tree view data return View(); } //get all files and show them in tree view public string GetAllFiles() { var fileslist = db.Files.Include(f => f.Lesson).ToList().GroupBy(f => f.LessonId); List<TreeViewNode> nodes = new List<TreeViewNode>(); fileslist.ForEach(f => { var tree = new TreeModel(); tree.nodes = new List<TreeModel>(); string lessonname = null; f.ForEach(g => { nodes.Add(new TreeViewNode { id = g.FileId.ToString() + "-" + f.Key.ToString(), parent =f.Key.ToString(), text =g.FileName.ToString() }); lessonname = g.Lesson.LessonName; }); nodes.Add(new TreeViewNode { id = f.Key.ToString(), parent = "#", text = lessonname }); }); return JsonConvert.SerializeObject(nodes); } [HttpPost] public ActionResult DownloadFiles(string selectedItems) { var allFilesPath= GetAllFilesPath(GetAllFilesId(selectedItems)); return DownloadZipFile(allFilesPath); } //get selected files path public List<string> GetAllFilesPath(List<int> fileidlist) { var allFilesPath = db.Files.Where(f => fileidlist.Contains(f.FileId)).Select(f=>f.FilePath).ToList(); return allFilesPath; } //get selected files id public List<int> GetAllFilesId(string selectedItems) { List<TreeViewNode> items = JsonConvert.DeserializeObject<List<TreeViewNode>>(selectedItems); List<int> fileidlist = new List<int>(); items.ForEach(i => { fileidlist.Add(Int32.Parse(i.id)); }); return fileidlist; } //DownloadZipFile public FileResult DownloadZipFile(List<string> allFilesPath) { var fileName = string.Format("{0}_files.zip", DateTime.Today.Date.ToString("dd-MM-yyyy") + "_1"); var temppath = Server.MapPath("~/TempFiles/"); if (!Directory.Exists(temppath)) { Directory.CreateDirectory(temppath); } var tempOutPutPath = Path.Combine(temppath, fileName); using (ZipOutputStream s = new ZipOutputStream(System.IO.File.Create(tempOutPutPath))) { s.SetLevel(9); // 0-9, 9 being the highest compression byte[] buffer = new byte[4096]; var filepathList = new List<string>(); for (int i = 0; i < allFilesPath.Count; i++) { string currentfilepath= Path.Combine(Server.MapPath("~/"+ @allFilesPath[i])); ZipEntry entry = new ZipEntry(Path.GetFileName(currentfilepath)); entry.DateTime = DateTime.Now; entry.IsUnicodeText = true; s.PutNextEntry(entry); using (FileStream fs = System.IO.File.OpenRead(currentfilepath)) { int sourceBytes; do { sourceBytes = fs.Read(buffer, 0, buffer.Length); s.Write(buffer, 0, sourceBytes); } while (sourceBytes > 0); } } s.Finish(); s.Flush(); s.Close(); } byte[] finalResult = System.IO.File.ReadAllBytes(tempOutPutPath); if (System.IO.File.Exists(tempOutPutPath)) System.IO.File.Delete(tempOutPutPath); if (finalResult == null || !finalResult.Any()) throw new Exception(String.Format("No Files found with Image")); return File(finalResult, "application/zip", fileName); } }
Index
<div id="jstree"> </div> @using (Html.BeginForm("DownloadFiles", "Home", FormMethod.Post)) { <input type="hidden" name="selectedItems" id="selectedItems" /> <input type="submit" value="Submit" /> } <link href="~/Content/themes/default/style.min.css" rel="stylesheet" /> @section scripts{ <script src="~/Scripts/jstree.min.js"></script> <script> $(function () { $('#jstree').on('changed.jstree', function (e, data) { setdata(data); }).jstree({ "core": { "themes": { "variant": "large" }, "data": @Html.Raw(ViewBag.Json) }, "checkbox": { "keep_selected_style": false }, "plugins": ["wholerow", "checkbox"], }); function setdata(data) {//Set the selected file var i, j; var selectedItems = []; for (i = 0, j = data.selected.length; i < j; i++) { var id = data.selected[i]; if (id.indexOf('-') != -1) { selectedItems.push({ text: data.instance.get_node(data.selected[i]).text, id: id.split("-")[0], parent: id.split("-")[1] }); } } $('#selectedItems').val(JSON.stringify(selectedItems)); } }); </script> }
Here is the result.
Best Regards,
YihuiSun
Thursday, August 27, 2020 10:55 AM - This is an MVC project and uses EF, you can modify it according to your own actual situation.
-
User2097725413 posted
Hi YihuiSun
Thanks for the solution.
I added the same functionality in Web API to download a zip file containing list of different folders.
For e.g.
List<string> lstPath = new List<string>();
lstPath.Add("/ScoContainer/Package0");
lstPath.Add("/ScoContainer/Package1");
lstPath.Add("/ScoContainer/Package2");And passed the list (lstPath) to function - DownloadZipFile
Here I want to download Package0, Package1, Package2 including subfolders and files into one zip file.
At a stage in code - using (FileStream fs = System.IO.File.OpenRead(currentfilepath)), I am getting exception as
System.UnauthorizedAccessException: 'Access to the path 'F:\Practise projects\WebApi_DownloadZip\WebApi_DownloadZip\ScoContainer\Package0' is denied.'
For this I set full permission the folder "ScoContainer" but not working.
Can you tell me, how to give access to folders so that it will create a zip to download?
Thanks,
Uday Mahajan
Monday, August 31, 2020 7:57 PM -
User1686398519 posted
Hi Uday Mahajan,
- File. Open is used to open an existing file for reading.This means that
currentfilepath is the path of the file that needs to be read,
not the path of the folder.When you read the path of the folder, the error "System.UnauthorizedAccessException" will appear.
- It seems that the file you want to download will still be in the corresponding subdirectory. You need to modify the code like this.
-
ZipEntry entry = new ZipEntry(allFilesPath[i]); //For example, the value of allFilesPath[i]): Files/Lesson2/File4.docx
-
- I wrote an example of webapi, and use mvc to call webpai, you can refer to it.
Controller in MVC
[HttpPost] public async Task<ActionResult> DownloadFiles(string selectedItems) { var allFilesPath= GetAllFilesPath(GetAllFilesId(selectedItems)); var client = new HttpClient(); string url = "https://localhost:44333/api/DownloadFiles/DownloadZipFile"; client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); HttpContent senddatacontent = new StringContent(JsonConvert.SerializeObject(allFilesPath), Encoding.UTF8, "application/json"); HttpResponseMessage response = await client.PostAsync(url, senddatacontent); if (response.IsSuccessStatusCode) { byte[] finalResult = await response.Content.ReadAsByteArrayAsync(); string filetype= response.Content.Headers.ContentType.ToString(); var fileName = response.Content.Headers.ContentDisposition.FileName; return File(finalResult, filetype, fileName); } return Content("fail"); }
Controller in WebApi
public class DownloadFilesController : ApiController
{
public HttpResponseMessage DownloadZipFile([FromBody] List<string> allFilesPath) { HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK); var fileName = string.Format("{0}_files.zip", DateTime.Today.Date.ToString("dd-MM-yyyy") + "_1"); var temppath = HostingEnvironment.MapPath("~/TempFiles/"); if (!Directory.Exists(temppath)) { Directory.CreateDirectory(temppath); } var tempOutPutPath = Path.Combine(temppath, fileName); using (ZipOutputStream s = new ZipOutputStream(System.IO.File.Create(tempOutPutPath))) { s.SetLevel(9); byte[] buffer = new byte[4096]; var filepathList = new List<string>(); for (int i = 0; i < allFilesPath.Count; i++) { string currentfilepath = Path.Combine(HostingEnvironment.MapPath("~/" + @allFilesPath[i])); ZipEntry entry = new ZipEntry(allFilesPath[i]); entry.DateTime = DateTime.Now; entry.IsUnicodeText = true; s.PutNextEntry(entry); using (FileStream fs = System.IO.File.OpenRead(currentfilepath)) { int sourceBytes; do { sourceBytes = fs.Read(buffer, 0, buffer.Length); s.Write(buffer, 0, sourceBytes); } while (sourceBytes > 0); } } s.Finish(); s.Flush(); s.Close(); } byte[] finalResult =File.ReadAllBytes(tempOutPutPath); if (File.Exists(tempOutPutPath)) File.Delete(tempOutPutPath); if (finalResult == null || !finalResult.Any()) throw new Exception(String.Format("No Files")); response.Content = new ByteArrayContent(finalResult); //Set the Response Content Length. response.Content.Headers.ContentLength = finalResult.ToArray().LongLength; //Set the Content Disposition Header Value and FileName. response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); response.Content.Headers.ContentDisposition.FileName = fileName; //Set the File Content Type. response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip"); return response; }
}Here is the result.
Best Regards,
YihuiSun
Wednesday, September 2, 2020 5:11 AM - File. Open is used to open an existing file for reading.This means that
currentfilepath is the path of the file that needs to be read,
not the path of the folder.When you read the path of the folder, the error "System.UnauthorizedAccessException" will appear.