mirror of
https://github.com/oatpp/oatpp.git
synced 2025-04-18 19:00:23 +08:00
Temporary file (#498)
* TemporaryFile: use shared file-handle. * Tests. Uncomment all.
This commit is contained in:
parent
e65a686982
commit
bc3d17750d
@ -105,6 +105,8 @@ add_library(oatpp
|
||||
oatpp/core/data/share/MemoryLabel.hpp
|
||||
oatpp/core/data/share/StringTemplate.cpp
|
||||
oatpp/core/data/share/StringTemplate.hpp
|
||||
oatpp/core/data/share/TemporaryFile.cpp
|
||||
oatpp/core/data/share/TemporaryFile.hpp
|
||||
oatpp/core/data/stream/BufferStream.cpp
|
||||
oatpp/core/data/stream/BufferStream.hpp
|
||||
oatpp/core/data/stream/ChunkedBuffer.cpp
|
||||
|
112
src/oatpp/core/data/share/TemporaryFile.cpp
Normal file
112
src/oatpp/core/data/share/TemporaryFile.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
/***************************************************************************
|
||||
*
|
||||
* Project _____ __ ____ _ _
|
||||
* ( _ ) /__\ (_ _)_| |_ _| |_
|
||||
* )(_)( /(__)\ )( (_ _)(_ _)
|
||||
* (_____)(__)(__)(__) |_| |_|
|
||||
*
|
||||
*
|
||||
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "TemporaryFile.hpp"
|
||||
|
||||
#include "oatpp/core/data/stream/BufferStream.hpp"
|
||||
#include "oatpp/encoding/Hex.hpp"
|
||||
#include "oatpp/core/utils/Random.hpp"
|
||||
|
||||
namespace oatpp { namespace data { namespace share {
|
||||
|
||||
TemporaryFile::FileHandle::~FileHandle() {
|
||||
if(fileName) {
|
||||
std::remove(fileName->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
oatpp::String TemporaryFile::concatDirAndName(const oatpp::String& dir, const oatpp::String& filename) {
|
||||
if(dir && dir->size() > 0) {
|
||||
auto lastChar = dir->data()[dir->size() - 1];
|
||||
if(lastChar != '/' && lastChar != '\\') {
|
||||
return dir + "/" + filename;
|
||||
}
|
||||
return dir + filename;
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
oatpp::String TemporaryFile::constructRandomFilename(const oatpp::String& dir, v_int32 randomWordSizeBytes) {
|
||||
|
||||
std::unique_ptr<v_char8[]> buff(new v_char8[randomWordSizeBytes]);
|
||||
utils::random::Random::randomBytes(buff.get(), randomWordSizeBytes);
|
||||
data::stream::BufferOutputStream s(randomWordSizeBytes * 2 + 4);
|
||||
encoding::Hex::encode(&s, buff.get(), randomWordSizeBytes, encoding::Hex::ALPHABET_LOWER);
|
||||
s << ".tmp";
|
||||
|
||||
return concatDirAndName(dir, s.toString());
|
||||
|
||||
}
|
||||
|
||||
TemporaryFile::TemporaryFile(const oatpp::String& tmpDirectory, v_int32 randomWordSizeBytes)
|
||||
: m_handle(std::make_shared<FileHandle>(constructRandomFilename(tmpDirectory, randomWordSizeBytes)))
|
||||
{}
|
||||
|
||||
TemporaryFile::TemporaryFile(const oatpp::String& tmpDirectory, const oatpp::String& tmpFileName)
|
||||
: m_handle(std::make_shared<FileHandle>(concatDirAndName(tmpDirectory, tmpFileName)))
|
||||
{}
|
||||
|
||||
oatpp::String TemporaryFile::getFullFileName() {
|
||||
if(m_handle) {
|
||||
return m_handle->fileName;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
data::stream::FileOutputStream TemporaryFile::openOutputStream() {
|
||||
if(m_handle) {
|
||||
return data::stream::FileOutputStream(m_handle->fileName->c_str(), "wb", m_handle);
|
||||
}
|
||||
throw std::runtime_error("[oatpp::data::share::TemporaryFile::openOutputStream()]: Error. FileHandle is NOT initialized.");
|
||||
}
|
||||
|
||||
data::stream::FileInputStream TemporaryFile::openInputStream() {
|
||||
if(m_handle) {
|
||||
return data::stream::FileInputStream(m_handle->fileName->c_str(), m_handle);
|
||||
}
|
||||
throw std::runtime_error("[oatpp::data::share::TemporaryFile::openInputStream()]: Error. FileHandle is NOT initialized.");
|
||||
}
|
||||
|
||||
std::shared_ptr<data::stream::FileOutputStream> TemporaryFile::openOutputStreamShared() {
|
||||
if(m_handle) {
|
||||
return std::make_shared<data::stream::FileOutputStream>(m_handle->fileName->c_str(), "wb", m_handle);
|
||||
}
|
||||
throw std::runtime_error("[oatpp::data::share::TemporaryFile::openOutputStreamShared()]: Error. FileHandle is NOT initialized.");
|
||||
}
|
||||
|
||||
std::shared_ptr<data::stream::FileInputStream> TemporaryFile::openInputStreamShared() {
|
||||
if(m_handle) {
|
||||
return std::make_shared<data::stream::FileInputStream>(m_handle->fileName->c_str(), m_handle);
|
||||
}
|
||||
throw std::runtime_error("[oatpp::data::share::TemporaryFile::openInputStreamShared()]: Error. FileHandle is NOT initialized.");
|
||||
}
|
||||
|
||||
bool TemporaryFile::moveFile(const oatpp::String& fullFileName) {
|
||||
if(m_handle) {
|
||||
return std::rename(m_handle->fileName->c_str(), fullFileName->c_str()) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}}}
|
134
src/oatpp/core/data/share/TemporaryFile.hpp
Normal file
134
src/oatpp/core/data/share/TemporaryFile.hpp
Normal file
@ -0,0 +1,134 @@
|
||||
/***************************************************************************
|
||||
*
|
||||
* Project _____ __ ____ _ _
|
||||
* ( _ ) /__\ (_ _)_| |_ _| |_
|
||||
* )(_)( /(__)\ )( (_ _)(_ _)
|
||||
* (_____)(__)(__)(__) |_| |_|
|
||||
*
|
||||
*
|
||||
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef oatpp_data_share_TemporaryFile_hpp
|
||||
#define oatpp_data_share_TemporaryFile_hpp
|
||||
|
||||
#include "oatpp/core/data/stream/FileStream.hpp"
|
||||
#include "oatpp/core/Types.hpp"
|
||||
|
||||
namespace oatpp { namespace data { namespace share {
|
||||
|
||||
/**
|
||||
* Temporary file - the file which gets deleted when the destructor is called
|
||||
* (more precisely when all copies of the same `TemporaryFile` object deleted). <br>
|
||||
* The `TemporaryFile` object internally stores a `shared_ptr` to a file handle.
|
||||
* When file handle deleted it also deletes the underlying file. <br>
|
||||
* Thus it's safe to copy `TemporaryFile` object and you may treat `TemporaryFile` object
|
||||
* as a shared_ptr to a temporary file.
|
||||
*/
|
||||
class TemporaryFile {
|
||||
private:
|
||||
|
||||
/*
|
||||
* Shared handle.
|
||||
* File is deleted on handle destroy.
|
||||
*/
|
||||
struct FileHandle {
|
||||
|
||||
oatpp::String fileName;
|
||||
|
||||
FileHandle(const oatpp::String& fullFileName)
|
||||
: fileName(fullFileName)
|
||||
{}
|
||||
|
||||
~FileHandle();
|
||||
};
|
||||
|
||||
private:
|
||||
static oatpp::String concatDirAndName(const oatpp::String& dir, const oatpp::String& filename);
|
||||
static oatpp::String constructRandomFilename(const oatpp::String& dir, v_int32 randomWordSizeBytes);
|
||||
private:
|
||||
std::shared_ptr<FileHandle> m_handle;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
TemporaryFile() = default;
|
||||
|
||||
/**
|
||||
* Constructor. <br>
|
||||
* Create temporary file with a random name in the `tmpDirectory`. <br>
|
||||
* The actual file will be created only after first write to that file. <br>
|
||||
* Example of the generated random file name: `f7c6ecd44024ff31.tmp`.
|
||||
* @param tmpDirectory - directory where to create a temporary file.
|
||||
* @param randomWordSizeBytes - number of random bytes to generate file name.
|
||||
*/
|
||||
TemporaryFile(const oatpp::String& tmpDirectory, v_int32 randomWordSizeBytes = 8);
|
||||
|
||||
/**
|
||||
* Constructor.<br>
|
||||
* Create temporary file with the `tmpFileName` name in the `tmpDirectory`. <br>
|
||||
* @param tmpDirectory - directory where to create a temporary file.
|
||||
* @param tmpFileName - predefined name for the temporary file.
|
||||
*/
|
||||
TemporaryFile(const oatpp::String& tmpDirectory, const oatpp::String& tmpFileName);
|
||||
|
||||
/**
|
||||
* Get full name of a temporary file.
|
||||
* @return
|
||||
*/
|
||||
oatpp::String getFullFileName();
|
||||
|
||||
/**
|
||||
* Open output stream to a temporary file. <br>
|
||||
* *Note: stream also captures file-handle. The temporary file won't be deleted until the stream is deleted.*
|
||||
* @return - &id:oatpp::data::stream::FileOutputStream;.
|
||||
*/
|
||||
data::stream::FileOutputStream openOutputStream();
|
||||
|
||||
/**
|
||||
* Open input stream to a temporary file.
|
||||
* *Note: stream also captures file-handle. The temporary file won't be deleted until the stream is deleted.*
|
||||
* @return - &id:oatpp::data::stream::FileInputStream;.
|
||||
*/
|
||||
data::stream::FileInputStream openInputStream();
|
||||
|
||||
/**
|
||||
* Open output stream to a temporary file. <br>
|
||||
* *Note: stream also captures file-handle. The temporary file won't be deleted until the stream is deleted.*
|
||||
* @return - `std::shared_ptr` to &id:oatpp::data::stream::FileOutputStream;.
|
||||
*/
|
||||
std::shared_ptr<data::stream::FileOutputStream> openOutputStreamShared();
|
||||
|
||||
/**
|
||||
* Open input stream to a temporary file.
|
||||
* *Note: stream also captures file-handle. The temporary file won't be deleted until the stream is deleted.*
|
||||
* @return - `std::shared_ptr` &id:oatpp::data::stream::FileInputStream;.
|
||||
*/
|
||||
std::shared_ptr<data::stream::FileInputStream> openInputStreamShared();
|
||||
|
||||
/**
|
||||
* Move payload to a different file. <br>
|
||||
* @param fullFileName - full-file-name. Note: all the parent folders must exist.
|
||||
* @return - `true` - file was successfully moved, `false` - otherwise.
|
||||
*/
|
||||
bool moveFile(const oatpp::String& fullFileName);
|
||||
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif //oatpp_data_share_TemporaryFile_hpp
|
@ -32,14 +32,24 @@ namespace oatpp { namespace data{ namespace stream {
|
||||
|
||||
oatpp::data::stream::DefaultInitializedContext FileInputStream::DEFAULT_CONTEXT(data::stream::StreamType::STREAM_FINITE);
|
||||
|
||||
FileInputStream::FileInputStream(std::FILE* file, bool ownsFile)
|
||||
FileInputStream::FileInputStream(FileInputStream&& other)
|
||||
: m_file(other.m_file)
|
||||
, m_ownsFile(other.m_ownsFile)
|
||||
, m_ioMode(other.m_ioMode)
|
||||
{
|
||||
other.m_file = nullptr;
|
||||
other.m_ownsFile = false;
|
||||
}
|
||||
|
||||
FileInputStream::FileInputStream(std::FILE* file, bool ownsFile, const std::shared_ptr<void>& captureData)
|
||||
: m_file(file)
|
||||
, m_ownsFile(ownsFile)
|
||||
, m_ioMode(IOMode::ASYNCHRONOUS)
|
||||
, m_capturedData(captureData)
|
||||
{}
|
||||
|
||||
FileInputStream::FileInputStream(const char* filename)
|
||||
: FileInputStream(std::fopen(filename, "rb"), true)
|
||||
FileInputStream::FileInputStream(const char* filename, const std::shared_ptr<void>& captureData)
|
||||
: FileInputStream(std::fopen(filename, "rb"), true, captureData)
|
||||
{
|
||||
if(!m_file) {
|
||||
OATPP_LOGE("[oatpp::data::stream::FileInputStream::FileInputStream(filename)]", "Error. Can't open file '%s'.", filename);
|
||||
@ -48,9 +58,7 @@ FileInputStream::FileInputStream(const char* filename)
|
||||
}
|
||||
|
||||
FileInputStream::~FileInputStream() {
|
||||
if(m_ownsFile && m_file) {
|
||||
std::fclose(m_file);
|
||||
}
|
||||
this->close();
|
||||
}
|
||||
|
||||
std::FILE* FileInputStream::getFile() {
|
||||
@ -59,7 +67,10 @@ std::FILE* FileInputStream::getFile() {
|
||||
|
||||
v_io_size FileInputStream::read(void *data, v_buff_size count, async::Action& action) {
|
||||
(void) action;
|
||||
return std::fread(data, 1, count, m_file);
|
||||
if(m_file != nullptr) {
|
||||
return std::fread(data, 1, count, m_file);
|
||||
}
|
||||
return oatpp::IOError::BROKEN_PIPE;
|
||||
}
|
||||
|
||||
void FileInputStream::setInputStreamIOMode(IOMode ioMode) {
|
||||
@ -74,19 +85,51 @@ Context& FileInputStream::getInputStreamContext() {
|
||||
return DEFAULT_CONTEXT;
|
||||
}
|
||||
|
||||
void FileInputStream::close() {
|
||||
if(m_ownsFile && m_file) {
|
||||
std::fclose(m_file);
|
||||
}
|
||||
}
|
||||
|
||||
FileInputStream& FileInputStream::operator=(FileInputStream&& other) {
|
||||
|
||||
if(this != &other) {
|
||||
close();
|
||||
}
|
||||
|
||||
m_file = other.m_file;
|
||||
m_ownsFile = other.m_ownsFile;
|
||||
m_ioMode = other.m_ioMode;
|
||||
|
||||
other.m_file = nullptr;
|
||||
other.m_ownsFile = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileOutputStream
|
||||
|
||||
oatpp::data::stream::DefaultInitializedContext FileOutputStream::DEFAULT_CONTEXT(data::stream::StreamType::STREAM_FINITE);
|
||||
|
||||
FileOutputStream::FileOutputStream(std::FILE* file, bool ownsFile)
|
||||
FileOutputStream::FileOutputStream(FileOutputStream&& other)
|
||||
: m_file(other.m_file)
|
||||
, m_ownsFile(other.m_ownsFile)
|
||||
, m_ioMode(other.m_ioMode)
|
||||
{
|
||||
other.m_file = nullptr;
|
||||
other.m_ownsFile = false;
|
||||
}
|
||||
|
||||
FileOutputStream::FileOutputStream(std::FILE* file, bool ownsFile, const std::shared_ptr<void>& captureData)
|
||||
: m_file(file)
|
||||
, m_ownsFile(ownsFile)
|
||||
, m_ioMode(IOMode::ASYNCHRONOUS)
|
||||
, m_capturedData(captureData)
|
||||
{}
|
||||
|
||||
FileOutputStream::FileOutputStream(const char* filename, const char* mode)
|
||||
: FileOutputStream(std::fopen(filename, mode), true)
|
||||
FileOutputStream::FileOutputStream(const char* filename, const char* mode, const std::shared_ptr<void>& captureData)
|
||||
: FileOutputStream(std::fopen(filename, mode), true, captureData)
|
||||
{
|
||||
if(!m_file) {
|
||||
OATPP_LOGE("[oatpp::data::stream::FileOutputStream::FileOutputStream(filename, mode)]", "Error. Can't open file '%s'.", filename);
|
||||
@ -95,9 +138,7 @@ FileOutputStream::FileOutputStream(const char* filename, const char* mode)
|
||||
}
|
||||
|
||||
FileOutputStream::~FileOutputStream() {
|
||||
if(m_ownsFile && m_file) {
|
||||
std::fclose(m_file);
|
||||
}
|
||||
this->close();
|
||||
}
|
||||
|
||||
std::FILE* FileOutputStream::getFile() {
|
||||
@ -121,4 +162,26 @@ Context& FileOutputStream::getOutputStreamContext() {
|
||||
return DEFAULT_CONTEXT;
|
||||
}
|
||||
|
||||
void FileOutputStream::close() {
|
||||
if(m_ownsFile && m_file) {
|
||||
std::fclose(m_file);
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream& FileOutputStream::operator=(FileOutputStream&& other) {
|
||||
|
||||
if(this != &other) {
|
||||
close();
|
||||
}
|
||||
|
||||
m_file = other.m_file;
|
||||
m_ownsFile = other.m_ownsFile;
|
||||
m_ioMode = other.m_ioMode;
|
||||
|
||||
other.m_file = nullptr;
|
||||
other.m_ownsFile = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
}}}
|
||||
|
@ -41,20 +41,33 @@ private:
|
||||
std::FILE* m_file;
|
||||
bool m_ownsFile;
|
||||
IOMode m_ioMode;
|
||||
private:
|
||||
std::shared_ptr<void> m_capturedData;
|
||||
public:
|
||||
|
||||
FileInputStream(const FileInputStream&) = delete;
|
||||
FileInputStream &operator=(const FileInputStream&) = delete;
|
||||
|
||||
/**
|
||||
* Move constructor.
|
||||
* @param other
|
||||
*/
|
||||
FileInputStream(FileInputStream&& other);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param file - file.
|
||||
* @param ownsFile - if `true` then call close on `FileInputStream` destruction.
|
||||
* @param captureData - capture auxiliary data to not get deleted until it's done with the stream.
|
||||
*/
|
||||
FileInputStream(std::FILE* file, bool ownsFile);
|
||||
FileInputStream(std::FILE* file, bool ownsFile, const std::shared_ptr<void>& captureData = nullptr);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param filename - name of the file.
|
||||
* @param captureData - capture auxiliary data to not get deleted until it's done with the stream.
|
||||
*/
|
||||
FileInputStream(const char* filename);
|
||||
FileInputStream(const char* filename, const std::shared_ptr<void>& captureData = nullptr);
|
||||
|
||||
/**
|
||||
* Virtual destructor.
|
||||
@ -96,6 +109,13 @@ public:
|
||||
*/
|
||||
Context& getInputStreamContext() override;
|
||||
|
||||
/**
|
||||
* Close file.
|
||||
*/
|
||||
void close();
|
||||
|
||||
FileInputStream& operator=(FileInputStream&& other);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@ -108,21 +128,34 @@ private:
|
||||
std::FILE* m_file;
|
||||
bool m_ownsFile;
|
||||
IOMode m_ioMode;
|
||||
private:
|
||||
std::shared_ptr<void> m_capturedData;
|
||||
public:
|
||||
|
||||
FileOutputStream(const FileOutputStream&) = delete;
|
||||
FileOutputStream &operator=(const FileOutputStream&) = delete;
|
||||
|
||||
/**
|
||||
* Move constructor.
|
||||
* @param other
|
||||
*/
|
||||
FileOutputStream(FileOutputStream&& other);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param file - file.
|
||||
* @param ownsFile - if `true` then call close on `FileInputStream` destruction.
|
||||
* @param captureData - capture auxiliary data to not get deleted until it's done with the stream.
|
||||
*/
|
||||
FileOutputStream(std::FILE* file, bool ownsFile);
|
||||
FileOutputStream(std::FILE* file, bool ownsFile, const std::shared_ptr<void>& captureData = nullptr);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param filename - name of the file.
|
||||
* @param mode - ("wb" - create new/override, "ab" - create new/append).
|
||||
* @param captureData - capture auxiliary data to not get deleted until it's done with the stream.
|
||||
*/
|
||||
FileOutputStream(const char* filename, const char* mode = "wb");
|
||||
FileOutputStream(const char* filename, const char* mode = "wb", const std::shared_ptr<void>& captureData = nullptr);
|
||||
|
||||
/**
|
||||
* Virtual destructor.
|
||||
@ -164,6 +197,13 @@ public:
|
||||
*/
|
||||
Context& getOutputStreamContext() override;
|
||||
|
||||
/**
|
||||
* Close file.
|
||||
*/
|
||||
void close();
|
||||
|
||||
FileOutputStream& operator=(FileOutputStream&& other);
|
||||
|
||||
};
|
||||
|
||||
}}}
|
||||
|
@ -62,7 +62,6 @@
|
||||
#include "oatpp/core/async/Coroutine.hpp"
|
||||
#include "oatpp/core/Types.hpp"
|
||||
|
||||
#include "oatpp/core/concurrency/SpinLock.hpp"
|
||||
#include "oatpp/core/base/Environment.hpp"
|
||||
|
||||
#include <iostream>
|
||||
@ -218,21 +217,21 @@ void runTests() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
|
||||
oatpp::base::Environment::init();
|
||||
|
||||
|
||||
runTests();
|
||||
|
||||
|
||||
/* Print how much objects were created during app running, and what have left-probably leaked */
|
||||
/* Disable object counting for release builds using '-D OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */
|
||||
std::cout << "\nEnvironment:\n";
|
||||
std::cout << "objectsCount = " << oatpp::base::Environment::getObjectsCount() << "\n";
|
||||
std::cout << "objectsCreated = " << oatpp::base::Environment::getObjectsCreated() << "\n\n";
|
||||
|
||||
|
||||
OATPP_ASSERT(oatpp::base::Environment::getObjectsCount() == 0);
|
||||
|
||||
|
||||
oatpp::base::Environment::destroy();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user