An example showing how to upload a VTFx file to Ceetron Cloud (using Qt).
Get the upload ID from the user
The user needs to provide his/her unique upload ID to upload the file. The user will need an account on https://cloud.ceetron.com, so you need to send the user either to the https://cloud.ceetron.com/signup page or https://cloud.ceetron.com/login page. From the "My Account" page the user can press the button "Show and copy NN's Upload ID" to get the upload ID. You should only ask for this once, and then store it somewhere safe in your app.
The Upload ID is a GUID, e.g.:
765ba418-58ea-4c0a-afd1-fb4ff8f410ee
Pass this to the example from the command prompt >sendToCloud.exe -uploadId 'uploadId'
Export the model to a VTFx file.
Next, we need to create the VTFx file for upload. In this example we use the same model as in the Minimal Example. You should export to a temporary file, which should be deleted after upload.
Upload the file to Ceetron Cloud.
Use the following REST API call to upload the file to Ceetron:
API: https://cloud.ceetron.com/api/v1/models
Params:
- uploadId: The users' Upload ID.
- uploadApp: The name of your app.
- name: The name of the model. This will become the default name in the My Models page of the user on Ceetron Cloud.
Example:
https://cloud.ceetron.com/api/v1/models?uploadId=765ba418-58ea-4c0a-afd1-fb4ff8f410ee&uploadApp=My%20App&name=Demo%20Model
Note:* The uploadApp and name parameters needs to be percent encoded to form a valid URI
The upload must be done with a "multipart/form-data" HTTPS POST, where the VTFx is sent as a file with the name "vtfx".
Content-Disposition: form-data; name="vtfx"; filename="upload.vtfx"
Content-Type: application/octet-stream
QSCMain.cpp
QString cloudURL = "https://cloud.ceetron.com";
{
const cee::Str fileName =
"ExampleMinimal.vtfx";
if (!file->
create(fileName, fileSettings))
{
std::cout << "#ERROR: Could not create file." << std::endl;
return "";
}
const float NODES_PART[] =
{
0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f
};
std::vector<float> nodesPart(NODES_PART, NODES_PART + sizeof(NODES_PART) / sizeof(NODES_PART[0]));
if (!nodeBlock->setNodes(nodesPart))
{
std::cout << "#ERROR: Error setting node block." << std::endl;
return "";
}
{
std::cout << "#ERROR: Error writing node block." << std::endl;
return "";
}
const int CONNECTS_PART[] =
{
0, 1, 2, 3, 4, 5, 6, 7
};
std::vector<int> elementNodes(CONNECTS_PART, CONNECTS_PART + sizeof(CONNECTS_PART) / sizeof(CONNECTS_PART[0]));
{
std::cout << "#ERROR: Error adding elements." << std::endl;
return "";
}
{
std::cout << "#ERROR: Error writing element block." << std::endl;
return "";
}
size_t geoIndex = 0;
int partId = 1;
{
std::cout << "#ERROR: Error adding element block." << std::endl;
return "";
}
{
std::cout << "#ERROR: Error writing geometry block." << std::endl;
return "";
}
{
std::cout << "#ERROR: Error writing info block." << std::endl;
return "";
}
{
std::cout << "#ERROR: Error adding state info." << std::endl;
return "";
}
{
std::cout << "#ERROR: Error writing state info block." << std::endl;
return "";
}
{
std::cout << "#ERROR: Error closing file." << std::endl;
return "";
}
std::cout <<
"#INFO: Exported successfully to file: " << fileName.
toStdString() << std::endl;
return fileName;
}
int main(int argc, char *argv[])
{
setlocale(LC_NUMERIC,"C");
QCoreApplication app(argc, argv);
CloudUploadTracker* uploadTracker = new CloudUploadTracker(&app, cloudURL);
unsigned int licenseKeyA = 0x00;
unsigned int licenseKeyB = 0x00;
#ifndef QT_NO_OPENSSL
if (!QSslSocket::supportsSsl())
{
std::cerr << "Your system and/or Qt version does not support secure connections (HTTPS).\nAre the OpenSSL dynamic libraries missing?" << std::endl;
return EXIT_FAILURE;
}
#else
std::cerr << "Your Qt version was not built with OpenSSL support." << std::endl;
return EXIT_FAILURE;
#endif
QString uploadId = "";
QStringList arguments = app.arguments();
if (arguments.size() == 1)
{
std::cout << "sendToCloud v. 1.0-0" << std::endl << "---------------------" << std::endl << std::endl;
std::cout << "Note: App will upload to: " << cloudURL.toStdString() << std::endl << std::endl;
std::cout << "Usage:" << std::endl << std::endl;
std::cout << "> sendToCloud -uploadId MY_UPLOAD_ID" << std::endl << std::endl;
return EXIT_FAILURE;
}
int uploadIdIndex = -1;
for (int i = 1; i < arguments.size(); ++i)
{
if (uploadIdIndex == i)
{
uploadId = arguments[i];
uploadIdIndex = -1;
}
else if (arguments[i].toLower() == "-uploadid")
{
uploadIdIndex = i + 1;
}
}
if (uploadId.isEmpty())
{
std::cout << "No Upload ID provided (-uploadId)" << std::endl;
return EXIT_FAILURE;
}
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
multiPart->setBoundary("boundary_Ceetron89906a4aJn1kvlAG52SFG2AD");
QHttpPart vtfxPart;
vtfxPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
vtfxPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"vtfx\" filename=\"upload.vtfx\""));
QFile *file = new QFile(cee::qt::UtilsCore::toQString(filename));
if (!file->open(QIODevice::ReadOnly))
{
std::cout <<
"Error opening file: " << filename.
toStdString() << std::endl;
return EXIT_FAILURE;
}
vtfxPart.setBodyDevice(file);
file->setParent(multiPart);
multiPart->append(vtfxPart);
QString uploadApp = "Ceetron Export Cloud Example";
QString caseName = "Minimal example";
QString urlString = QString(cloudURL + "/api/v1/models?uploadId=%1&uploadApp=%2&name=%3").arg(QString(QUrl::toPercentEncoding(uploadId))).arg(QString(QUrl::toPercentEncoding(uploadApp))).arg(QString(QUrl::toPercentEncoding(caseName)));
std::cout << "#INFO: Using URL: " << urlString.toStdString() << std::endl;
QUrl url(urlString);
QNetworkRequest request(url);
QNetworkAccessManager* manager = new QNetworkAccessManager(NULL);
QNetworkReply* reply = manager->post(request, multiPart);
multiPart->setParent(reply);
QObject::connect(reply, SIGNAL(finished()), uploadTracker, SLOT(cloudUploadFinished()));
QObject::connect(reply, SIGNAL(uploadProgress(qint64, qint64)), uploadTracker, SLOT(updateUploadProgress(qint64, qint64)));
QObject::connect(uploadTracker, SIGNAL(finished()), &app, SLOT(quit()));
std::cout << "#INFO: Uploading file to Ceetron Cloud..." << std::endl;
int result = app.exec();
std::cout << "#INFO: Done." << std::endl;
return result;
}
QSCCloudTracker.h
Small class to track upload progress and completion
class CloudUploadTracker : public QObject
{
Q_OBJECT
public:
CloudUploadTracker(QObject *parent, QString cloudURL) : QObject(parent), m_cloudURL(cloudURL) {}
public slots:
void cloudUploadFinished()
{
QNetworkReply* reply = (QNetworkReply*)sender();
const int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString message;
if (httpStatusCode > 0)
{
QString responseJson = reply->readAll();
if (httpStatusCode == 200)
{
const QString modelKey = parseTopLevelJsonValue(responseJson, "modelKey");
const QString viewerUrl = m_cloudURL + "/v/" + modelKey;
message = "#SUCCESS;" + viewerUrl + "; model key:" + modelKey;
}
else
{
const QString failReason = parseTopLevelJsonValue(responseJson, "message");
const QString apiErrorCode = parseTopLevelJsonValue(responseJson, "apiErrorCode");
message = "#FAIL; Error: " + QString("%1 - httpStatusCode:%2 - apiErrorCode:%3").arg(failReason).arg(httpStatusCode).arg(apiErrorCode);
}
}
else
{
message = QString("#ERROR; Network Error During Upload - %1").arg(reply->errorString());
}
std::cout << message.toStdString() << std::endl;
reply->deleteLater();
emit finished();
}
void updateUploadProgress(qint64 bytesSent, qint64 bytesTotal)
{
float pctDone = 100.0f*(float)bytesSent/(float)bytesTotal;
std::cout << "#PROGRESS; " << pctDone << "; " << bytesSent << " of " << bytesTotal << " bytes. " << pctDone << "% done." << std::endl;
}
signals:
void finished();
private:
QString parseTopLevelJsonValue(const QString json, const QString key)
{
QString valString = "";
const QString quotedKey = QString("\"%1\"").arg(key);
const int keyIdx = json.indexOf(quotedKey);
if (keyIdx >= 0)
{
int startQuote = json.indexOf("\"", keyIdx + quotedKey.length());
int endQuote = json.indexOf("\"", startQuote + 1);
if (startQuote >= 0 && endQuote > (startQuote + 1))
{
valString = json.mid(startQuote + 1, endQuote - startQuote - 1);
}
}
return valString;
}
private:
QString m_cloudURL;
};