Unlock a world of possibilities! Login now and discover the exclusive benefits awaiting you.
Hi everyone, we're setting up a small environment where I will be automating the transfer of Qlik Sense Apps to different engines on different servers. Using the APIs I've been able to Import, Publish and get a ticket for Exporting an app, but not been able to use the APIs to Export.
I'm probably missing something obvious, but it's not clear to me with the Export call:
How do I deal with the body of the request returned? I've written the content directly to the file system, but it's not a proper app when opened, so I'm not sure if I have to remove headers, decompress, or even look on the file system on the server or somewhere else (as you have to specify your path) to get the file export working. It looks like the text returned is almost a Qlik Sense App (comparing it with the original).
I've also got task triggering working in C# via this example (and got the other API calls working): Working with REST API to trigger reload tasks
My code is:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strServerName + @":4242/qrs/download/app/" + strAppID + "/" + strRequestGUID + "/" + strFilename + "?xrfkey=" + xrfkey);
request.Method = "GET";
request.Accept = "application/vnd.qlik.sense.app";
request.Headers.Add("X-Qlik-xrfkey", xrfkey);
request.Headers.Add("id", strAppID);// Add the certificate to the request and provide the user to execute as
request.ClientCertificates.Add(SenseCert);
request.Headers.Add("X-Qlik-User", strUserStringForQlik);//Send the request
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
//So at this point, we have a stream when put into raw text looks like
// a Qlik Sense App, but is not a Qlik Sense app.using (StreamWriter sw = new StreamWriter(strFilename, false, Encoding.UTF8))
{
StreamReader sr = new StreamReader(stream, Encoding.UTF8);//We write the data to a file here, but it won't load in Qlik Sense
while (!sr.EndOfStream)
{string strLinedata = sr.ReadLine();
sw.WriteLine(strLinedata);
}
}}
}
So after some more research, I have figured it out. TL;DR the data returned in the request is a byte stream, and should be written as a byte stream not as a string.
I had thought of using a byte stream, but since I have never used one of those before I used the wrong code, and it didn't work, so I kept on trying with the streamwriter. The streamwriter writes strings, and hence when I looked at the file, the text parts looked like a proper .QVF, but nothing else did.
Honestly I'm surprised there's no easier way of writing a byte stream in C#, but at least it works!
Code assume previous setup has been done, insert this over the top of the response:
//Send the request
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
// Create the local file
Stream localStream = File.Create(strFilename);
int bytesProcessed = 0;// Allocate a 1k buffer
byte[] buffer = new byte[1024];
int bytesRead;// Simple do/while loop to read from stream until
// no bytes are returned
do
{
// Read data (up to 1k) from the stream
bytesRead = stream.Read(buffer, 0, buffer.Length);// Write the data to the local file
localStream.Write(buffer, 0, bytesRead);// Increment total bytes processed
bytesProcessed += bytesRead;
} while (bytesRead > 0);
}
}
I guess it should have been obvious, but clearly the obvious takes weeks to figure out whilst other things are being done!
david.maurice You must subclass and override one or more functions.
class MyWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
request.ClientCertificates.Add(SenseCert);
return request;
}
}
Use MyWebClient as below to export app.
using (var client = new MyWebClient())
{
client.DownloadFile(strServerName + @":4242/qrs/download/app/" + strAppID + "/" + strRequestGUID + "/" + strFilename + "?xrfkey=" + xrfkey, "app.qvf");
}
That is my fault, I did not include the Certificate code in my example, (there is nothing proprietary in it so I should have).
// locate the client certificate and accept it as trusted
X509Certificate2 SenseCert = new X509Certificate2(strCertificatePath, "", X509KeyStorageFlags.MachineKeySet);
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
and this certificate "SenseCert" is then passed in. It is the code I use for the other requests I make.
So after some more research, I have figured it out. TL;DR the data returned in the request is a byte stream, and should be written as a byte stream not as a string.
I had thought of using a byte stream, but since I have never used one of those before I used the wrong code, and it didn't work, so I kept on trying with the streamwriter. The streamwriter writes strings, and hence when I looked at the file, the text parts looked like a proper .QVF, but nothing else did.
Honestly I'm surprised there's no easier way of writing a byte stream in C#, but at least it works!
Code assume previous setup has been done, insert this over the top of the response:
//Send the request
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
// Create the local file
Stream localStream = File.Create(strFilename);
int bytesProcessed = 0;// Allocate a 1k buffer
byte[] buffer = new byte[1024];
int bytesRead;// Simple do/while loop to read from stream until
// no bytes are returned
do
{
// Read data (up to 1k) from the stream
bytesRead = stream.Read(buffer, 0, buffer.Length);// Write the data to the local file
localStream.Write(buffer, 0, bytesRead);// Increment total bytes processed
bytesProcessed += bytesRead;
} while (bytesRead > 0);
}
}
I guess it should have been obvious, but clearly the obvious takes weeks to figure out whilst other things are being done!
Easier way of writing a byte stream or downloading a file in C#, is as mentioned in my above answer using WebClient.
class MyWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
request.ClientCertificates.Add(SenseCert);
return request;
}
}
Use MyWebClient as below to export app.
using (var client = new MyWebClient())
{
client.DownloadFile(strServerName + @":4242/qrs/download/app/" + strAppID + "/" + strRequestGUID + "/" + strFilename + "?xrfkey=" + xrfkey, "app.qvf");
}
I've had a look, and yes your answer is a lot neater, but we're actually using the same classes.
WebClient does use its internal WebRequest class (which you override to add the cert) to access resources
https://msdn.microsoft.com/en-us/library/system.net.webclient(v=vs.110).aspx
I use HTTPWebrequest, which inherits WebRequest
https://msdn.microsoft.com/en-us/library/system.net.webrequest(v=vs.110).aspx
With HTTPWebrequest, you get to add certs and other HTTP properties directly, but in your code you get to use the DownloadFile method of WebClient directly, rather than having to loop through items to get and write the file as I do
I'd argue that you could setup a HTTPWebRequest class with parameters passed in and then override WebClients internal WebRequest to get all the functionality in a single method, but for me that's when I have some free time *laughs*