BlogUmbraco and Things
Bulk Media Downloads
Author: Nigel Wilson
Posted: 31 January 2011
I have been working on a new site for the past four months in my
spare time.
It is a digital photography site where members can feature their
photos, enter competitions, etc.
One requirement for the site was to provide functionality that
enables the bulk download of images, ie competition entries,
etc.
Initially I thought the Export Media
Package would be perfect, but after some additional
requirements gathering it was obvious that the club needed more
detail than simply the images, e.g. Member Name, Image Name, Judges
Scores, etc.
First up, I created a new media type (copy of
the Image media type) and added a member picker property to it.
This provides the information relating to who entered what
photos.
The next step was to create a dashboard
control. Tim Geyssens dashboard
post is what I referenced to create this.
Then I created a user control with the
following:
- Dropdown List of all competitions
- A button to download a CSV file of entry data
- A button to download a ZIP file of the images
Whilst it would be ideal to include all the downloads into 1
button, for the sake of simplicity and getting the job done I chose
to keep them separate.
And the following is the code for the user control:
ASCX File
<ul>
<li>
<asp:label runat="server" AssociatedControlID="competitions" ID="lblCompetitions">Select Competition</asp:label>
<asp:DropDownList ID="competitions" runat="server">
</asp:DropDownList>
<asp:RequiredFieldValidator ID="rfvCompetitions" ValidationGroup="compDownload" runat="server" ControlToValidate="competitions" InitialValue="0" />
</li>
<li>
<asp:Button ID="btnDownloadCSV" runat="server" Text="Download CSV File of Entry Data" OnClick="btnDownloadCSV_CLick" ValidationGroup="compDownload" />
<asp:Button ID="btnDownloadZIP" runat="server" Text="Download ZIP File of Images" OnClick="btnDownloadZIP_CLick" ValidationGroup="compDownload" />
</li>
</ul>
Code Behind File
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using umbraco.cms.businesslogic.media;
using System.IO;
using umbraco.cms.businesslogic.member;
using ICSharpCode.SharpZipLib.Zip;
public partial class usercontrols_dashboards_competitionImageDownload : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
loadCurrentCompetitions();
}
protected void loadCurrentCompetitions()
{
if (!IsPostBack)
{
competitions.Items.Clear();
competitions.Items.Add(new ListItem("- - - Please Select - - -", "0"));
Media m = new Media(Convert.ToInt32(ConfigurationManager.AppSettings["mediaCompetitionParent"]));
foreach (Media iMg in m.Children)
{
competitions.Items.Add(new ListItem(iMg.Text.ToString(), iMg.Id.ToString()));
}
}
}
protected void btnDownloadCSV_CLick(object sender, EventArgs e)
{
int selectedCompID = Convert.ToInt32(competitions.SelectedValue);
string selectedCompName = competitions.SelectedItem.Text.Replace(" ", "");
Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/ms-excel";
Response.AddHeader("content-disposition", string.Format("attachment;filename={0}.csv", selectedCompName));
Response.Charset = "";
StringWriter stringwriter = new StringWriter();
stringwriter.Flush();
// First write the headers.
stringwriter.Write("Image Name,Image Path,Member ID,Member Name,Judge 1 Score,Judge 2 Score,Judge 3 Score,Total Score");
stringwriter.Write(stringwriter.NewLine);
Media m = new Media(selectedCompID);
foreach (Media iMg in m.Children)
{
stringwriter.Write("\"" + iMg.Text + "\",\"" + iMg.getProperty("umbracoFile").Value.ToString() + "\",\"" + iMg.getProperty("memberID").Value + "\",\"");
try
{
Member currMember = new Member(Convert.ToInt32(iMg.getProperty("memberID").Value));
stringwriter.Write(currMember.Text);
}
catch
{
stringwriter.Write("");
}
stringwriter.Write("\",\"" + iMg.getProperty("judgeScore1").Value + "\",\"" + iMg.getProperty("judgeScore2").Value + "\",\"" + iMg.getProperty("judgeScore3").Value + "\"");
stringwriter.Write(stringwriter.NewLine);
}
Response.Write(stringwriter.ToString());
Response.End();
}
protected void btnDownloadZIP_CLick(object sender, EventArgs e)
{
int selectedCompID = Convert.ToInt32(competitions.SelectedValue);
string selectedCompName = competitions.SelectedItem.Text.Replace(" ", "");
Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/zip";
Response.AppendHeader("content-disposition", "attachment; filename=\"" + selectedCompName + ".zip\"");
Response.CacheControl = "Private";
Response.Cache.SetExpires(DateTime.Now.AddMinutes(3)); // or put a timestamp in the filename in the content-disposition
byte[] buffer = new byte[4096];
ZipOutputStream zipOutputStream = new ZipOutputStream(Response.OutputStream);
zipOutputStream.SetLevel(3); //0-9, 9 being the highest level of compression
Media m = new Media(selectedCompID);
foreach (Media iMg in m.Children)
{
String fileName = iMg.getProperty("umbracoFile").Value.ToString();
fileName = fileName.Substring(0, fileName.LastIndexOf('.')) + "_competition" + fileName.Substring(fileName.LastIndexOf('.'));
try
{
Stream fs = File.OpenRead(Server.MapPath("~" + fileName)); // or any suitable inputstream
fileName = Path.GetFileName(fileName);
ZipEntry entry = new ZipEntry(ZipEntry.CleanName(fileName));
entry.Size = fs.Length;
// Setting the Size provides WinXP built-in extractor compatibility,
// but if not available, you can set zipOutputStream.UseZip64 = UseZip64.Off instead.
zipOutputStream.PutNextEntry(entry);
int count = fs.Read(buffer, 0, buffer.Length);
while (count > 0)
{
zipOutputStream.Write(buffer, 0, count);
count = fs.Read(buffer, 0, buffer.Length);
if (!Response.IsClientConnected)
{
break;
}
Response.Flush();
}
fs.Close();
}
catch { }
}
zipOutputStream.Close();
Response.Flush();
Response.End();
}
}
Whilst it makes total sense now, a pleasant surprise when
putting this together was that Umbraco already uses the
ICSharpCode.SharpZipLib.Zip library and so it was a simple task to
"wire up" this functionality.
Future improvements should include:
- Build this into a compiled DLL file
- Make the code recursive to provide for sub folders.
However for the site I am building it works fine and at some
stage I may look to improve on it.
Feel free to drop me
an email if you read this and (a) get thoroughly confused by my
ramlbings, or (b) are interested in learning more, or (c) keen to
help improve the code so it can be packaged for others to use.
Cheers from down under.
Nigel
No comments recorded for post.