Introduction
Many times a developer needs to create files dynamically in the WebPart background code and download them at user request. However,
a
Response.End()
statement
in WebPart does not work as expected and allows download only once or not at all. This
article/tip gives simple steps to achieve dynamic / "on the fly" / at
runtime construction
of files and allow download through web part multiple times.
Background
I was developing a web part where images are to be displayed in thumbnail format and information related to those images is to be downloaded if user clicks the image.
The image URL and description was stored in a SharePoint list.
I began with reading SPList
items one by one and to save the list read operation on user click,
I kept the image description in a hidden DIV (.infoDiv
in code below) near the image (.image in code below) tag. Added a
__doPostBack
using jQuery
in the image click function registered through jQuery.
Using the code
$(".image").click(function(){
var info = $(".image").next(".infoDiv").html();
$("#<%=infoHidden.ClientID %>").val(info);
__doPostBack('IMGInfoSent','');
});
The above code snippet was passing the image information through an ASP.NET hidden field viz.
infoHidden
. When the __doPostBack
is received in Page Load event,
the __EVENTTARGET
information is read from the Request
object and file was created using the information in the hidden field and written back to the Response object using code below:
Please note, the content in infoHidden
field were encoded in base64 format to avoid any HTML encoding issues due to content itself.
if (Page.IsPostBack)
{
if (Request["__EVENTTARGET"] != null)
{
if (Request["__EVENTTARGET"] == "IMGInfoSent")
{
byte[] bytes = Convert.FromBase64String(infoHidden.Value);
string normalString = Encoding.Default.GetString(bytes);
byte[] fileContent = Encoding.Default.GetBytes(normalString);
infoHidden.Value = string.Empty;
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("Content-Disposition",
"attachment;filename=myfile.txt");
Response.ContentType = "text/plain";
Response.BinaryWrite(fileContent);
Response.End();
return;
}
}
}
The C# code above was decoding the base64 value from the hidden variable and writing to a text file.
This solution was working well till user clicks an image once. For the first time user clicks on an image and the download starts, however, for second time if user click on another or same image nothing was happening. During debug and later on Google I found that the issue is the Response variable modifications done for file download. The
Response.End()
and Response Header modifications are blocking the SharePoint web part to allow download once again and even it stopped the
__doPostBack
call from second request onward.
Then suddenly I remembered that we earlier created a download helper file to address another
such issue where we wanted the SharePoint to forcefully download a PDF instead of opening it in the browser window. And decided to apply similar solution to my problem and write
an article over here.
So....Here is the solution...
- Right click on the project solution in Visual Studio 2010 or above. (I Assume your web part is under same solution.)
- Add new Application Page in the solution. It will create a folder tree like below:
Layouts --> <Folder with your Project Name> --> An ASPX page.
- Add following code in the Page Load method of this ASPX page.
if (Request["fn"] != null && Request["fd"] != null)
{
string fileName = Request["fn"] == string.Empty ? "ImageInfo" : Request["fn"];
string fileData = Request["fd"];
byte[] bytes = Convert.FromBase64String(fileData);
string normalString = Encoding.Default.GetString(bytes);
byte[] fileContent = Encoding.Default.GetBytes(normalString);
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("Content-Disposition",
"attachment;filename=" + fileName +".txt");
Response.ContentType = "text/plain";
Response.BinaryWrite(fileContent);
Response.End();
}
The fn and fd are query string parameters to share file name and file data with the application page.
- Added an anchor tag outside the image thumbnail and assigned href attribute value as in code below (in the web part):
href = "/_layouts/<Folder with your Project Name>/<Name of your application page>.aspx?fn="+
Server.UrlEncode(myFileName)+"&fd="+Server.UrlEncode(fileData);
Thus, the anchor tag has passed file name and file data to the application page and in the Page Load the application page will initiate the file download process.
Now, irrespective of how many times user click on an image, file will be downloaded each time by creating "on the fly" every time. The query string parameters will pass file name and file data in base64 format to the application page each time.
Hope this helps someone......or......myself in future.