diff --git a/changelog/1.3.0.md b/changelog/1.3.0.md index 341e1f90..75abd4cd 100644 --- a/changelog/1.3.0.md +++ b/changelog/1.3.0.md @@ -14,6 +14,7 @@ Contents: - [ConnectionProviderSwitch](#connectionproviderswitch) - [Proper Server Stoppage](#proper-server-stoppage) - [TemporaryFile](#temporaryfile) +- [Better Multipart](#better-multipart) - [Response::getBody()](#responsegetbody) - [data::stream::FIFOStream](#datastreamfifostream) - [data::stream::BufferedInputStream](#datastreambufferedinputstream) @@ -243,7 +244,7 @@ Now call to `HttpConnectionHandler::stop()`, `AsyncHttpConnectionHandler::stop() ## TemporaryFile -Introduce `oatpp::data::share::TemporaryFile`. +Introduce `oatpp::data::resource::TemporaryFile`. Use-case: @@ -251,20 +252,72 @@ Temporary file resolves concurrency issues during file uploads. Also, a temporary file ensures that partially uploaded (due to errors/exceptions) resources will be automatically deleted at the end of the block. ```cpp +#include "oatpp/core/data/resource/TemporaryFile.hpp" + +... + ENDPOINT("POST", "/upload", upload, REQUEST(std::shared_ptr, request)) { - oatpp::data::share::TemporaryFile tmp("/tmp"); // create random file in '/tmp' folder - - auto stream = tmp.openOutputStream(); - request->transferBody(&stream); // transfer body to temporary file - - tmp.moveFile("/path/to/permanent/storage/avatar.png"); // move file to permanent storage - + /* create random file in '/tmp' folder */ + oatpp::data::resource::TemporaryFile tmp("/tmp"); + + /* transfer body to temporary file */ + request->transferBody(tmp.openOutputStream()); + + /* move file to permanent storage */ + OATPP_ASSERT_HTTP(tmp.moveFile("/path/to/permanent/storage/avatar.png"), Status::CODE_500, "Failed to save file") + + /* return 200 */ return createResponse(Status::CODE_200, "OK"); } ``` +## Better Multipart + +Multipart API has been changed and improved. +Now it's possible to upload multiple files using `TemporaryFile` and keep track of +all parts and their corresponding data resources. + +```cpp +#include "oatpp/web/mime/multipart/TemporaryFileProvider.hpp" +#include "oatpp/web/mime/multipart/Reader.hpp" +#include "oatpp/web/mime/multipart/PartList.hpp" + +... + +namespace multipart = oatpp::web::mime::multipart; + +... + +ENDPOINT("POST", "upload", upload, + REQUEST(std::shared_ptr, request)) +{ + + /* create multipart object */ + multipart::PartList multipart(request->getHeaders()); + + /* create multipart reader */ + multipart::Reader multipartReader(&multipart); + + /* setup reader to stream parts to a temporary files by default */ + multipartReader.setDefaultPartReader(multipart::createTemporaryFilePartReader("/Users/leonid/Documents/tmp")); + + /* upload multipart data */ + request->transferBody(&multipartReader); + + /* list all parts and locations to corresponding temporary files */ + auto parts = multipart.getAllParts(); + for(auto& p : parts) { + OATPP_LOGD("part", "name=%s, location=%s", p->getName()->c_str(), p->getPayload()->getLocation()->c_str()); + } + + /* return 200 */ + return createResponse(Status::CODE_200, "OK"); + +} +``` + ## Response::getBody() `oatpp::web::protocol::http::outgoing::Response` has a new method `getBody()` to retreive the body of the response. diff --git a/src/oatpp/web/protocol/http/incoming/Request.cpp b/src/oatpp/web/protocol/http/incoming/Request.cpp index b2f08ee7..482bedd4 100644 --- a/src/oatpp/web/protocol/http/incoming/Request.cpp +++ b/src/oatpp/web/protocol/http/incoming/Request.cpp @@ -138,12 +138,12 @@ const data::Bundle& Request::getBundle() const { return m_bundle; } -void Request::transferBody(data::stream::WriteCallback* writeCallback) const { - m_bodyDecoder->decode(m_headers, m_bodyStream.get(), writeCallback, m_connection.get()); +void Request::transferBody(const base::ObjectHandle& writeCallback) const { + m_bodyDecoder->decode(m_headers, m_bodyStream.get(), writeCallback.get(), m_connection.get()); } -void Request::transferBodyToStream(oatpp::data::stream::OutputStream* toStream) const { - m_bodyDecoder->decode(m_headers, m_bodyStream.get(), toStream, m_connection.get()); +void Request::transferBodyToStream(const base::ObjectHandle& toStream) const { + m_bodyDecoder->decode(m_headers, m_bodyStream.get(), toStream.get(), m_connection.get()); } oatpp::String Request::readBodyToString() const { diff --git a/src/oatpp/web/protocol/http/incoming/Request.hpp b/src/oatpp/web/protocol/http/incoming/Request.hpp index 2a6a7356..82630cdd 100644 --- a/src/oatpp/web/protocol/http/incoming/Request.hpp +++ b/src/oatpp/web/protocol/http/incoming/Request.hpp @@ -240,13 +240,13 @@ public: * Read body chunk by chunk and pass chunks to the `writeCallback`. * @param writeCallback - &id:oatpp::data::stream::WriteCallback;. */ - void transferBody(data::stream::WriteCallback* writeCallback) const; + void transferBody(const base::ObjectHandle& writeCallback) const; /** * Stream content of the body-stream to toStream * @param toStream */ - void transferBodyToStream(oatpp::data::stream::OutputStream* toStream) const; + void transferBodyToStream(const base::ObjectHandle& toStream) const; /** * Transfer body stream to string @@ -261,7 +261,7 @@ public: * @return DTO */ template - Wrapper readBodyToDto(data::mapping::ObjectMapper* objectMapper) const { + Wrapper readBodyToDto(const base::ObjectHandle& objectMapper) const { return objectMapper->readFromString(m_bodyDecoder->decodeToString(m_headers, m_bodyStream.get(), m_connection.get())); } diff --git a/src/oatpp/web/protocol/http/incoming/Response.cpp b/src/oatpp/web/protocol/http/incoming/Response.cpp index 2fa809db..72644950 100644 --- a/src/oatpp/web/protocol/http/incoming/Response.cpp +++ b/src/oatpp/web/protocol/http/incoming/Response.cpp @@ -103,12 +103,12 @@ std::shared_ptr Response::getBodyDecoder() co return m_bodyDecoder; } -void Response::transferBody(data::stream::WriteCallback* writeCallback) const { - m_bodyDecoder->decode(m_headers, m_bodyStream.get(), writeCallback, m_connection.get()); +void Response::transferBody(const base::ObjectHandle& writeCallback) const { + m_bodyDecoder->decode(m_headers, m_bodyStream.get(), writeCallback.get(), m_connection.get()); } -void Response::transferBodyToStream(oatpp::data::stream::OutputStream* toStream) const { - m_bodyDecoder->decode(m_headers, m_bodyStream.get(), toStream, m_connection.get()); +void Response::transferBodyToStream(const base::ObjectHandle& toStream) const { + m_bodyDecoder->decode(m_headers, m_bodyStream.get(), toStream.get(), m_connection.get()); } oatpp::String Response::readBodyToString() const { diff --git a/src/oatpp/web/protocol/http/incoming/Response.hpp b/src/oatpp/web/protocol/http/incoming/Response.hpp index 68a08eb9..2736555a 100644 --- a/src/oatpp/web/protocol/http/incoming/Response.hpp +++ b/src/oatpp/web/protocol/http/incoming/Response.hpp @@ -185,7 +185,7 @@ public: * Get raw body stream. * @return - raw body stream as &id:oatpp::data::stream::InputStream;. */ - std::shared_ptr getBodyStream() const; + std::shared_ptr getBodyStream() const; /** * Get body decoder configured for this response. @@ -198,14 +198,14 @@ public: * Read body chunk by chunk and pass chunks to the `writeCallback`. * @param writeCallback - &id:oatpp::data::stream::WriteCallback;. */ - void transferBody(data::stream::WriteCallback* writeCallback) const; + void transferBody(const base::ObjectHandle& writeCallback) const; /** * Decode and transfer body to toStream. * Use case example - stream huge body directly to file using relatively small buffer. * @param toStream - pointer to &id:oatpp::data::stream::OutputStream;. */ - void transferBodyToStream(oatpp::data::stream::OutputStream* toStream) const; + void transferBodyToStream(const base::ObjectHandle& toStream) const; /** * Decode and read body to &id:oatpp::String;. @@ -220,8 +220,8 @@ public: * @return - deserialized DTO object. */ template - Wrapper readBodyToDto(oatpp::data::mapping::ObjectMapper* objectMapper) const { - return m_bodyDecoder->decodeToDto(m_headers, m_bodyStream.get(), m_connection.get(), objectMapper); + Wrapper readBodyToDto(const base::ObjectHandle& objectMapper) const { + return m_bodyDecoder->decodeToDto(m_headers, m_bodyStream.get(), m_connection.get(), objectMapper.get()); } // Async @@ -240,7 +240,7 @@ public: * @param toStream - `std::shared_ptr` to &id:oatpp::data::stream::OutputStream;. * @return - &id:oatpp::async::CoroutineStarter;. */ - oatpp::async::CoroutineStarter transferBodyToStreamAsync(const std::shared_ptr& toStream) const; + oatpp::async::CoroutineStarter transferBodyToStreamAsync(const std::shared_ptr& toStream) const; /** * Same as &l:Response::readBodyToString (); but Async. @@ -258,7 +258,7 @@ public: */ template oatpp::async::CoroutineStarterForResult - readBodyToDtoAsync(const std::shared_ptr& objectMapper) const { + readBodyToDtoAsync(const std::shared_ptr& objectMapper) const { return m_bodyDecoder->decodeToDtoAsync(m_headers, m_bodyStream, m_connection, objectMapper); }