setCacheDuration on Wicket DownloadLink - excel

I am currently using a downloadLink in Wicket to allow a user to download a created excel file, and then to be deleted afterwards. When this is done over SSL IE gives me an error:
"Unable to download.
Internet Explorer was unable to open this site. The requested site is either unavailable or cannot be found. Please try again later. "
here:
http://support.microsoft.com/kb/323308
after doing some reading from the above microsoft support link, it seems it is because
because it's over SSL, and I have
response.setHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store");
I set my downloadLink like so:
private void setupDownloadLink()
{
IModel excelFileModel = new AbstractReadOnlyModel()
{
public Object getObject()
{
return excelCreator();
}
};
auditDownloadlink = new DownloadLink("auditDownloadlink", excelFileModel);
auditDownloadlink.setOutputMarkupPlaceholderTag(true);
auditDownloadlink.setDeleteAfterDownload(true);
auditDownloadlink.setCacheDuration(Duration.NONE);
auditDownloadlink.setVisible(false);
findUserForm.add(auditDownloadlink);
}
However, it seems to work if I do: auditDownloadlink.setCacheDuration(Duration.Minute);
I guess I am confused on what is happening with this. Does the setCacheDuration mean how long after the file is created, it will be available for before it is deleted? Or does this mean how long in total the file will be available for from the start of declaring it?
inside the excelCreator() method I call File excelfile = new File("Access.xls"); and then go ahead and process all of the excel work and create the spreadsheet, then at the end of the method I call:
FileOutputStream output = new FileOutputStream(excelfile);
workbook.write(output);
output.close();
Will the duration time I set start from the moment I call File excelfile = new File("ssaUserIDAccess.xls")?
What is the best duration and setup I should use for this scenario? Because the files can get quit huge, and can take some time to create if it is big enough.
Thanks!

I do not remember the reason, but we had the same problem on SSL/IE and we just set the cache duration to 1 second that is enough. Just it cannot be NONE. Another solution we've never found.
auditDownloadlink.setCacheDuration(Duration.ONE_SECOND)

Related

How to save Excel file to local drive from browser

I've got an XPage that creates an Excel file using server-side javascript (thank to Russ Maher). I know how to save it to the C: drive if I'm running the XPage locally in a browser, but don't know how to save it to the user's machine when it's running on the server without first saving it to the server. The following code is used to save it from the server's perspective.
var fileOut = new java.io.FileOutputStream(directory+fileName);
xl.write(fileOut);
fileOut.close();
Any ideas how I can direct that to the user's drive?
Instead of writing the Excel workbook to a FileOutputStream, you should write it to a ByteArrayOutputStream:
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
xl.write(outputStream);
You probably need to use an XAgent to create the output and then link to the XAgent from your XPage. Maybe this blog entry by Declan Lynch combined with this answer on how to do it in a servlet can guide you in the right direction.
Paul Calhoun sent me some sample code that I massaged to get it to produce the spreadsheet that I wanted. I don't know what it was that he'd done that I hadn't, but, for now, I think this is the heart of the solution, just harnessing an OutputStream instead of either a FileOutputStream or ByteArrayOutputStream.
// The Faces Context global object provides access to the servlet environment via the external content
var extCont = facesContext.getExternalContext();
// The servlet's response object provides control to the response object
var pageResponse = extCont.getResponse();
//Get the output stream to stream binary data
var pageOutput = pageResponse.getOutputStream();
// Set the content type and headers
pageResponse.setContentType("application/x-ms-excel");
pageResponse.setHeader("Cache-Control", "no-cache");
pageResponse.setHeader("Content-Disposition","inline; filename=" + fileName);
//Write the output, flush the buffer and close the stream
wb.write(pageOutput);
pageOutput.flush();
pageOutput.close();
// Terminate the request processing lifecycle.
facesContext.responseComplete();
I will gladly provide help if someone else encounters this issue and, hopefully, by the time someone asks, I will understand more of what was different that worked....

Add a footer to an existing OpenXML spreadsheet

EDIT: There's a further detail I left out with my original post. The program is using a template stream rather than a concrete template for the ".Open" command. The template stream gets initialized with this code block:
public void Initialize(Stream templateStream)
{
spreadsheet = SpreadsheetDocument.Open(templateStream, true);
}
I'm still researching this, but does anyone know the implications of using a stream for the HeaderFooter object in OpenXML?
I'm new to OpenXML and still in the process of reading and learning what I can of this massive SDK. I've inherited a C# MVC.NET program that uses OpenXML to display information on an Excel spreadsheet and all of that is working, but I now need to add a footer to the same spreadsheet and I'm hitting some brick walls in my OpenXML knowledge.
I put the footer information I wanted into the spreadsheet, opened it up with the Open XML SDK Productivity Tool and found this code under <.x:oddFooter(OddFooter):
// Creates an OddFooter instance and adds its children.
public OddFooter GenerateOddFooter()
{
OddFooter oddFooter1 = new OddFooter();
oddFooter1.Text = "&L&\"Times New Roman,Regular\"Page &P of &N&C&\"Times New Roman,Regular\"Generated On: <Date/Time> Central&R&\"Times New Roman,Regular\"Report";
return oddFooter1;
}
And this code one level up under <>x:headerFooter(OddFooter):
// Creates an HeaderFooter instance and adds its children.
public HeaderFooter GenerateHeaderFooter()
{
HeaderFooter headerFooter1 = new HeaderFooter();
OddFooter oddFooter1 = new OddFooter();
oddFooter1.Text = "&L&\"Times New Roman,Regular\"Page &P of &N&C&\"Times New Roman,Regular\"Generated On: <Date/Time> Central&R&\"Times New Roman,Regular\"Report";
headerFooter1.Append(oddFooter1);
return headerFooter1;
}
Now I of course need to append the footer info somewhere, and this is where I'm stuck. In <>x:worksheet(Worksheet) I see this line of code:
worksheet1.Append(headerFooter1);
This looked easy enough, but when I looked back at the application code I found no worksheet object to append to. I thought I was close with the following line of code:
spreadsheet.WorkbookPart.Workbook.Append(headerFooter1);
but this yielded nothing. In the application I see a SpreadsheetDocument object and references to OpenXMLParts... do I need to get a spreadsheet part to append to? Or do I need to take a different approach with a Spreadsheet versus a worksheet object? Do I need to materialize the current worksheet and then append?
I have a feeling this has an easy solution, but as I said I'm still learning the SDK.
Thank you!
For those who don't know about the productivity tool, it is included with the SDK and can be downloaded from https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=5124
On my 64 bit machine, the install path to the tool was:
"C:\Program Files (x86)\Open XML SDK\V2.0\tool\OpenXmlSdkTool.exe"
You should be able to manually add a footer to a spreadsheet, and open it up with the tool and see the exact C# required to create the entire spreadsheet, including the footer. You can then remove the parts of the code that are unnecessary, like some of the styles that are automatically added.

SharePoint ItemAdded and SPFile.OpenBinary(), zero bytes

I have an event receiver tied to a SharePoint 2010 picture library. When a picture is uploaded, I want to open it for processing. Files uploaded with the web interface work fine, but files copied via Windows Explorer view return zero bytes. Simplified code below.
public override void ItemAdded(SPItemEventProperties properties)
{
SPListItem item = properties.ListItem;
SPFile file = item.File;
byte[] buffer = file.OpenBinary(); //buffer has zero bytes for files copied in Windows Explorer!
}
If I insert a delay before opening, it works.
public override void ItemAdded(SPItemEventProperties properties)
{
SPListItem item = properties.ListItem;
SPFile file = item.File;
System.Threading.Thread.Sleep(2000);
byte[] buffer = file.OpenBinary(); //buffer now populated correctly
}
But I though that ItemAdded was only called after everything was done, including file upload.
I also found that file.CanOpenFile(true) always returns true, whether or not OpenBinary works.
How can I make sure the file is ready to open before I call OpenBinary()?
I don't like the Thread.Sleep solution, because I'm sure larger files or a busier server would require more wait. The time required can't be predicted, and I don't want to loop and try again forever.
Update: I originally thought the failure to open was caused by larger files. Question has been updated to reflect the explorer view as the cause. I also find that Windows Explorer copy also triggers ItemUpdated (twice), and I am able to open the file here. A little messy to have 3 triggers, 2 calls to do 1 thing, so I am still open to suggestions.
I just encountered this issue today on SharePoint 2013. I've taken the suggestions listed here and improved upon them.
It's fine making the thread sleep for 2 seconds, but what happens when you have a large file? You're going to run into the same issue.
My code fix is as follows:
//Check if the SPContext is null since Explorer View isn't within the context
if (SPContext.Current == null)
{
//If the initial file length is 0, pause the thread for 2 seconds
if (properties.ListItem.File.Length == 0)
{
System.Threading.Thread.Sleep(2000);
//Since our item exists, run the GetItemById to instantiate a new and updated SPListItem object
var spFile = properties.List.GetItemById(properties.ListItemId);
//SharePoint places an Exclusive lock on the file while the data is being loaded into the file
while (spFile.File.LockType != SPFile.SPLockType.None)
{
System.Threading.Thread.Sleep(2000);
spFile = properties.List.GetItemById(properties.ListItemId);
//We need to check if the file exists, otherwise it will loop forever if someone decides to cancel the upload
if (!spFile.File.Exists)
return;
}
//If someone thought it was a good idea to actually load a 0 byte file, don't do anything else
if (spFile.File.Length == 0)
return;
}
}
I face the same Problem within SP2010 and SP2013. Any Idea how to solve this?
Somehow this have something to do with bigger files. Small Files work without any problems, bigger files (400kb) won't work always.
I have only one hint. If you copy & paste a file over windows explorer (WebDAV) to the Library the EventHandle (ItemAdded) will trigger as soon the File was created. But this doesn't mean the File is already filled with data. I saw this once, my debugger hit my breakpoint even while windows was still busy with the copyprocess.
Would be great to know when the copyprocess is finished. I thought i could do this by just do "spfile.openBinary()" and if its empty, just wait 2 sec and do it again until it will get something bigger than 0 bytes. But this doesnt work! It only work if you are waiting BEFORE you call openBinary() the first time, all other times of calling openBinary() lead to the same result.

How do I open a file with my application?

Ok, you know how in programs like Microsoft Excel, or Adobe Acrobat Reader you can click on a file in explorer and it will open with the associated program. That's what I want my application to do. Now, I know how to set up the file associations in Windows so that it knows the default program for each extension. My question is how do I get my application to open the file when I double click the file.
I've searched the web using google, I've searched the msdn site, and I've searched several forums including this one but I haven't found anything that explains how to accomplish this. I'm guessing it has something to do with the parameters of the main method but that's just a guess.
If someone can point me in the right direction I can take it from there. Thanks in advance for your help.
Shane
Setting up the associations in windows will send the filename to your application on the command line.
You need to read the event args in your applications main function in order to read the file path and be able to open it in your application.
See this and this to see how to access the command line arguments in your main method.
static void Main(string[] args)
{
System.Console.WriteLine("Number of command line parameters = {0}", args.Length);
foreach (string s in args)
{
System.Console.WriteLine(s);
}
}
When you open the file, with associations set as you described, your application will be started with the first argument containing the filepath to your file.
You can try this out in a simple way by printing out the args from your main method, after you open your application by clicking on the associated file. The 0th element should be the path to your file.
Now, if you successfully reached this point, the all you need to do now is read the contents of the given file. I'm sure you'll find more than plenty of resources here on how to do that.
I guess this is what you are looking for:
FileInfo fi = new FileInfo(sfd.FileName); //the file you clicked or saved just point
//to the right file location to determine
//full filename with location info
// opening file
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = #fi.FullName;
startInfo.WindowStyle = ProcessWindowStyle.Normal;
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
You will need to create registry-keys for your file-extension. This page describes well, which keys you'll need to set (see «3. How do I create file associations?»).

Upload document to specific folder in a SharePoint Document library using WebClient

I have some client side code that uploads an Outlook email to a document library and as long as the path is pointing to the root of the doc library it works just fine.
#"https://<server>/sites/<subweb>/<customer>/<teamweb>/<Documents>/" + docname;
is the projectUrl in this function :
public bool SaveMail(string filepath, string projectUrl)
{
try
{
using (WebClient webclient = new WebClient())
{
webclient.UseDefaultCredentials = true;
webclient.UploadFile(projectUrl, "PUT", filepath);
}
}
catch(Exception ex)
{
//TO DO Write the exception to the log file
return false;
}
return true;
}
but I have not been able to figur out how to upload to an existing folder i.e. "Emails" in the same document library.
Not even Google seems to know the answer :-)
Note: I know that I could use something like the Copy web service within SharePoint to move the file to its final destination, but that is more like a workaround.
When will I learn not to work that late into the night :-(
Sorry about that question. Igalse is right, I just needed to add "emails/" to the URL. I could swear that I had tried that, but then again it sure looks like I didn't.
With your code I just added /Emails/ to the projectUrl and the upload worked just fine. Have you tried that? Maybe you have permission problem.

Resources