[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