Slideshare API Upload
I've been experimenting a little with Slideshare as a mechanism to make our slidedecks more broadly available. I spent a fun morning last week emailing 15 slide decks to the Slideshare site. When they didn't appear by the next day I was a little surprised. When they hadn't appeared a week later I thought I'd better check what was going on.
The response I got was "upload by email and by url are disabled at the moment while we fix a few issues in our upload process". Fair enough. It might have been nice to put a message to that effect on the site or disable the "Email Upload" link or perhaps an auto-reply. Any of those might have saved me a few hours of effort. Anyway, what to do?
Slideshare does have an API and they've even built a .NET API Kit. A quick check of the documentation revealed an upload_slideshow resource. Great news! Unfortunately it's not exposed in the .NET API Kit. Booo! So I set about making some changes to the API Kit to allow me to do a bulk upload with titles, descriptions, tags etc (the big advantage over using the Bulk upload facility on the site).
It wasn't as easy as I thought as you have to manually craft the elements of the multipart/form-data request. In the end I came across this article on Code Project which gave me most of what I needed. After that it was a question of sniffing requests with Fiddler comparing the Slideshare Upload sample with my code. In the end I arrived at something that works. It's not terribly pretty but it works.
This is based on the .NET API Kit so download and unpack that first as a start point. In there you'll find sampleprogram.cs. I've added a simple class to represent the Slidedeck metadata and modified the sampleprogram class to drive the uploads from an XML file:
class Slidedeck
{
public string Title { get; set; }
public string Description { get; set; }
public string Tags { get; set; }
public string Filepath { get; set; }
}
class sampleprogram
{
static void Main(string[] args)
{
slideshare ss = new slideshare();
ss.initializeKeys(key, secret);
XDocument xdoc = XDocument.Load("UploadManifest.xml");
var query = from x in xdoc.Descendants("Slidedeck")
select new Slidedeck
{
Title = (string)x.Element("Title"),
Description = (string)x.Element("Description"),
Tags = (string)x.Element("Tags"),
Filepath = (string)x.Element("Filepath")
};
foreach (var item in query.ToList())
{
using (FileStream fs = File.Open(item.Filepath, FileMode.Open))
{
Console.WriteLine("Uploading {0}...", item.Title);
Console.WriteLine(
ss.uploadSlidedeck(username, password,
item.Title,
item.Description,
item.Tags,
fs));
Console.WriteLine("Finished Uploading {0}...", item.Title);
Console.WriteLine();
}
}
Console.ReadLine();
Then in the slideshare class in slideshare.cs I added a new uploadSlidedeck() method:
public string uploadSlidedeck(
string Username,
string Password,
string Title,
string Description,
string Tags,
FileStream Sourcefile)
{
Dictionary<string, string> parameters = new Dictionary<string, string>()
{
{ "api_key", api_key },
{"sharedsecret", shared_secret },
{"ts", timestamp},
{"hash", CalculateSHA1(shared_secret + timestamp, Encoding.UTF8)},
{"username", Username},
{"password", Password},
{"slideshow_title", Title},
{"slideshow_description", Description},
{"slideshow_tags", Tags}
};
return executePostCommand(
"https://www.slideshare.net/api/1/upload_slideshow",
parameters,
Sourcefile);
}
and in turn a modified version of the executePostCommand() method:
private string executePostCommand(
string uri,
Dictionary<string, string> parameters,
Stream contentStream)
{
if (api_key == null || shared_secret == null || timestamp == null)
return "Keys must be initialized first";
string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
webRequest.ContentType = "multipart/form-data; boundary=" + boundary;
webRequest.Method = "POST";
webRequest.Timeout = 600000;
Stream os = null;
try
{
StringBuilder sb = new StringBuilder();
// Add each of the parameters to the request
foreach (var item in parameters)
{
sb.Append("--");
sb.Append(boundary);
sb.Append("\r\n");
sb.Append("Content-Disposition: form-data; name=\"");
sb.Append(item.Key);
sb.Append("\"");
sb.Append("\r\n\r\n");
sb.Append(item.Value);
sb.Append("\r\n");
}
// Add the file upload itself to the request
sb.Append("--");
sb.Append(boundary);
sb.Append("\r\n");
sb.Append("Content-Disposition: form-data; name=\"");
sb.Append("slideshow_srcfile");
sb.Append("\"; filename=\"");
sb.Append("Test.ppt");
sb.Append("\"");
sb.Append("\r\n");
sb.Append("Content-Type: ");
sb.Append("application/vnd.ms-powerpoint");
sb.Append("\r\n");
sb.Append("\r\n");
string postHeader = sb.ToString();
byte[] postHeaderBytes = Encoding.UTF8.GetBytes(postHeader);
byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
// request will be postHeader (parameters) + contentStream (file) + boundary
webRequest.ContentLength = postHeaderBytes.Length +
contentStream.Length +
boundaryBytes.Length;
os = webRequest.GetRequestStream();
// Write the parameters to the request stream
os.Write(postHeaderBytes, 0, postHeaderBytes.Length);
// Copy from the file to the request stream
byte[] buffer = new byte[32768];
while (true)
{
int read = contentStream.Read(buffer, 0, buffer.Length);
if (read <= 0)
break;
os.Write(buffer, 0, read);
}
// Write the final boundary to the request stream
os.Write(boundaryBytes, 0, boundaryBytes.Length);
}
catch (WebException ex)
{
return ex.ToString();
}
finally
{
if (os != null)
{
os.Close();
}
}
try
{
WebResponse webResponse = webRequest.GetResponse();
if (webResponse == null)
{ return null; }
StreamReader sr = new StreamReader(webResponse.GetResponseStream());
return sr.ReadToEnd().Trim();
}
catch (WebException ex)
{
return ex.ToString();
}
}
And finally you need an XML file which I named UploadManifest.xml which describes the slidedecks you want to upload:
<?xml version="1.0" encoding="utf-8"?>
<Slidedecks>
<Slidedeck>
<Title></Title>
<Description></Description>
<Tags></Tags>
<Filepath></Filepath>
</Slidedeck>
</Slidedecks>
And believe it or not, it looks as though - just as I post this - my emailed slide decks (mailed on 2/12) have started to come through. At exactly the same time as my API uploads (bit of a coincidence). So I now have duplicates of everything. Argh...
Technorati Tags: slideshare,api,multipart/form-data,.net,upload