[Libreoffice-commits] online.git: wsd/ClientSession.cpp wsd/ClientSession.hpp wsd/LOOLWSD.cpp
Tor Lillqvist
tml at collabora.com
Thu Jul 12 16:27:42 UTC 2018
wsd/ClientSession.cpp | 43 ++++++++++++++++++++++++++++++++-
wsd/ClientSession.hpp | 12 ++++++++-
wsd/LOOLWSD.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++------
3 files changed, 110 insertions(+), 9 deletions(-)
New commits:
commit 405b66c8db71c314c2d26c4b18e9d74806c76bf1
Author: Tor Lillqvist <tml at collabora.com>
Date: Thu Jul 12 15:50:04 2018 +0300
Add a cache of "thumbnails" (PNG images) generated using the convert-to API
When asked to "convert" a document to a PNG image, i.e. what one can
call a thumbnail, check if we have a cached PNG for the same docuemnt
already, and in that case return it.
When we have done such a convert-to operation to PNG, save the result
in the cache for later re-use.
This change adds no thumbnail cache cleanup mechanism. That will have
to be implemented separately using a cron job or whatever.
There are further improvement possibilities: For instance, if the
document is of a type that contains an embedded thumbnail (like ODF),
just extract and return that. For ODF that embedded thumbnail even
already is in PNG format.
Change-Id: I882efe97acc1d81041dc7a4ccb222995940e4836
Reviewed-on: https://gerrit.libreoffice.org/57345
Reviewed-by: Tor Lillqvist <tml at collabora.com>
Tested-by: Tor Lillqvist <tml at collabora.com>
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index ee2a3f941..353b73263 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -13,6 +13,7 @@
#include <fstream>
+#include <Poco/File.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/StringTokenizer.h>
#include <Poco/URI.h>
@@ -35,15 +36,20 @@ using Poco::StringTokenizer;
ClientSession::ClientSession(const std::string& id,
const std::shared_ptr<DocumentBroker>& docBroker,
const Poco::URI& uriPublic,
- const bool readOnly) :
+ const bool readOnly,
+ const bool creatingPngThumbnail,
+ const std::string& thumbnailFile) :
Session("ToClient-" + id, id, readOnly),
_docBroker(docBroker),
_uriPublic(uriPublic),
+ _creatingPngThumbnail(creatingPngThumbnail),
+ _thumbnailFile(thumbnailFile),
_isDocumentOwner(false),
_isAttached(false),
_isViewLoaded(false),
_keyEvents(1)
{
+ assert(!creatingPngThumbnail || thumbnailFile != "");
const size_t curConnections = ++LOOLWSD::NumConnections;
LOG_INF("ClientSession ctor [" << getName() << "], current number of connections: " << curConnections);
}
@@ -722,6 +728,41 @@ bool ClientSession::handleKitToClientMessage(const char* buffer, const int lengt
response.set("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
HttpHelper::sendFile(_saveAsSocket, encodedFilePath, mimeType, response);
+
+ if (_creatingPngThumbnail)
+ {
+ // Save the created PNG "thumbnail" under a name constructed from the SHA1 of
+ // the document contents.
+
+ // FIXME: We could first try to simply hardlink the result as the thumbnail.
+
+ Poco::File(Poco::Path(_thumbnailFile).parent()).createDirectories();
+ std::ofstream thumbnail(_thumbnailFile, std::ios::binary);
+
+ if (thumbnail.is_open() && thumbnail.good())
+ {
+ std::ifstream result(resultURL.getPath(), std::ios::binary);
+ if (result.is_open() && result.good())
+ {
+ Poco::StreamCopier::copyStream(result, thumbnail);
+ if (!result.bad() && thumbnail.good())
+ {
+ LOG_TRC("Created cached thumbnail " << _thumbnailFile);
+ }
+ else
+ {
+ thumbnail.close();
+ unlink(_thumbnailFile.c_str());
+ }
+ }
+ }
+ else
+ {
+ if (thumbnail.is_open())
+ thumbnail.close();
+ unlink(_thumbnailFile.c_str());
+ }
+ }
}
// Conversion is done, cleanup this fake session.
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 2366ca78b..df7faa45f 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -26,7 +26,9 @@ public:
ClientSession(const std::string& id,
const std::shared_ptr<DocumentBroker>& docBroker,
const Poco::URI& uriPublic,
- const bool isReadOnly = false);
+ const bool isReadOnly = false,
+ const bool creatingPngThumbnail = false,
+ const std::string& thumbnailFile = "");
virtual ~ClientSession();
@@ -147,6 +149,14 @@ private:
/// URI with which client made request to us
const Poco::URI _uriPublic;
+ /// True iff this is a convert-to operation creating a PNG. We assume all such PNGs will be
+ /// usable as "thumbnails" for the document.
+ const bool _creatingPngThumbnail;
+
+ /// The pathname of the thumbnail file being created. Valid only if _creatingPngThumbnail is
+ /// true.
+ const std::string _thumbnailFile;
+
/// Whether this session is the owner of currently opened document
bool _isDocumentOwner;
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 93bd56e3f..1854d8cc2 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -49,14 +49,15 @@
#include <sstream>
#include <thread>
+#include <Poco/DateTimeFormatter.h>
+#include <Poco/DigestStream.h>
+#include <Poco/DirectoryIterator.h>
#include <Poco/DOM/AutoPtr.h>
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/DOMWriter.h>
#include <Poco/DOM/Document.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/NodeList.h>
-#include <Poco/DateTimeFormatter.h>
-#include <Poco/DirectoryIterator.h>
#include <Poco/Environment.h>
#include <Poco/Exception.h>
#include <Poco/File.h>
@@ -76,6 +77,7 @@
#include <Poco/Pipe.h>
#include <Poco/Process.h>
#include <Poco/SAX/InputSource.h>
+#include <Poco/SHA1Engine.h>
#include <Poco/StreamCopier.h>
#include <Poco/StringTokenizer.h>
#include <Poco/TemporaryFile.h>
@@ -1517,7 +1519,9 @@ static std::shared_ptr<ClientSession> createNewClientSession(const WebSocketHand
const std::string& id,
const Poco::URI& uriPublic,
const std::shared_ptr<DocumentBroker>& docBroker,
- const bool isReadOnly)
+ const bool isReadOnly,
+ const bool creatingPngThumbnail,
+ const std::string& thumbnailFile)
{
LOG_CHECK_RET(docBroker && "Null docBroker instance", nullptr);
try
@@ -1533,7 +1537,7 @@ static std::shared_ptr<ClientSession> createNewClientSession(const WebSocketHand
// In case of WOPI, if this session is not set as readonly, it might be set so
// later after making a call to WOPI host which tells us the permission on files
// (UserCanWrite param).
- return std::make_shared<ClientSession>(id, docBroker, uriPublic, isReadOnly);
+ return std::make_shared<ClientSession>(id, docBroker, uriPublic, isReadOnly, creatingPngThumbnail, thumbnailFile);
}
catch (const std::exception& exc)
{
@@ -2049,7 +2053,52 @@ private:
bool sent = false;
if (!fromPath.empty() && !format.empty())
{
- LOG_INF("Conversion request for URI [" << fromPath << "].");
+ LOG_INF("Conversion request for file [" << fromPath << "].");
+
+ std::string thumbnailFile;
+ if (format == "png")
+ {
+ // Check whether we already have a cached "thumbnail" for this document.
+
+ // FIXME: We could here check if the document is such that already contains an
+ // easily extractable thumbnail, like Thubnails/thumbnail.png in ODF documents,
+ // and extract and return that.
+
+ std::ifstream istr(fromPath, std::ios::binary);
+ if (istr.is_open() && istr.good())
+ {
+ Poco::SHA1Engine sha1;
+ Poco::DigestOutputStream dos(sha1);
+ Poco::StreamCopier::copyStream(istr, dos);
+ if (!istr.bad())
+ {
+ istr.close();
+ dos.close();
+ std::string digest = Poco::DigestEngine::digestToHex(sha1.digest());
+ thumbnailFile = LOOLWSD::Cache + "/thumbnails/" + digest.substr(0, 2) + "/"
+ + digest.substr(2, 2) + "/" + digest.substr(4) + ".png";
+
+ istr.open(thumbnailFile, std::ios::binary);
+ if (istr.is_open() && istr.good())
+ {
+ std::string png;
+ Poco::StreamCopier::copyToString(istr, png);
+ if (!istr.bad())
+ {
+ LOG_TRC("Found cached thumbnail " << thumbnailFile);
+
+ response.set("Content-Disposition", "attachment; filename=\"" + digest + ".png\"");
+ response.setContentType("image/png");
+ response.setContentLength(png.size());
+ socket->send(response);
+ socket->send(png.data(), png.size(), true);
+
+ return;
+ }
+ }
+ }
+ }
+ }
Poco::URI uriPublic = DocumentBroker::sanitizeURI(fromPath);
const std::string docKey = DocumentBroker::getDocKey(uriPublic);
@@ -2070,7 +2119,8 @@ private:
// Load the document.
// TODO: Move to DocumentBroker.
const bool isReadOnly = true;
- std::shared_ptr<ClientSession> clientSession = createNewClientSession(nullptr, _id, uriPublic, docBroker, isReadOnly);
+ std::shared_ptr<ClientSession> clientSession = createNewClientSession(nullptr, _id, uriPublic, docBroker,
+ isReadOnly, format == "png", thumbnailFile);
if (clientSession)
{
disposition.setMove([docBroker, clientSession, format]
@@ -2309,7 +2359,7 @@ private:
std::shared_ptr<DocumentBroker> docBroker = findOrCreateDocBroker(ws, url, docKey, _id, uriPublic);
if (docBroker)
{
- std::shared_ptr<ClientSession> clientSession = createNewClientSession(&ws, _id, uriPublic, docBroker, isReadOnly);
+ std::shared_ptr<ClientSession> clientSession = createNewClientSession(&ws, _id, uriPublic, docBroker, isReadOnly, false, "");
if (clientSession)
{
// Transfer the client socket to the DocumentBroker when we get back to the poll:
More information about the Libreoffice-commits
mailing list