Skip to content

Commit ad7973c

Browse files
authored
Added option to create UploadFile from data in memory (#2484)
1 parent 595ee83 commit ad7973c

5 files changed

Lines changed: 100 additions & 11 deletions

File tree

lib/inc/drogon/UploadFile.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,27 @@ class UploadFile
5656
}
5757
}
5858

59+
/// Constructor
60+
/**
61+
* @param data Pointer to the data
62+
* @param len Data length in bytes
63+
* @param fileName The file name provided to the server.
64+
* @param itemName The item name on the browser form.
65+
* @param contentType The Mime content type for the part
66+
*/
67+
explicit UploadFile(const void *data,
68+
const size_t len,
69+
const std::string &fileName = "memory.bin",
70+
const std::string &itemName = "file",
71+
ContentType contentType = CT_APPLICATION_OCTET_STREAM)
72+
: data_(data),
73+
len_(len),
74+
fileName_(fileName),
75+
itemName_(itemName),
76+
contentType_(contentType)
77+
{
78+
}
79+
5980
const std::string &path() const
6081
{
6182
return path_;
@@ -76,7 +97,19 @@ class UploadFile
7697
return contentType_;
7798
}
7899

100+
const void *data() const
101+
{
102+
return data_;
103+
}
104+
105+
const size_t dataLength() const
106+
{
107+
return len_;
108+
}
109+
79110
private:
111+
const void *data_ = nullptr;
112+
size_t len_ = 0;
80113
std::string path_;
81114
std::string fileName_;
82115
std::string itemName_;

lib/src/HttpRequestImpl.cc

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -323,21 +323,31 @@ void HttpRequestImpl::appendToBuffer(trantor::MsgBuffer *output) const
323323
content.append(type.data(), type.length());
324324
}
325325
content.append("\r\n\r\n");
326-
std::ifstream infile(utils::toNativePath(file.path()),
327-
std::ifstream::binary);
328-
if (!infile)
326+
327+
if (file.data() && file.dataLength() > 0)
329328
{
330-
LOG_ERROR << file.path() << " not found";
329+
content.append((const char *)file.data(),
330+
file.dataLength());
331331
}
332332
else
333333
{
334-
std::streambuf *pbuf = infile.rdbuf();
335-
std::streamsize filesize = pbuf->pubseekoff(0, infile.end);
336-
pbuf->pubseekoff(0, infile.beg); // rewind
337-
std::string str;
338-
str.resize(filesize);
339-
pbuf->sgetn(&str[0], filesize);
340-
content.append(std::move(str));
334+
std::ifstream infile(utils::toNativePath(file.path()),
335+
std::ifstream::binary);
336+
if (!infile)
337+
{
338+
LOG_ERROR << file.path() << " not found";
339+
}
340+
else
341+
{
342+
std::streambuf *pbuf = infile.rdbuf();
343+
std::streamsize filesize =
344+
pbuf->pubseekoff(0, infile.end);
345+
pbuf->pubseekoff(0, infile.beg); // rewind
346+
std::string str;
347+
str.resize(filesize);
348+
pbuf->sgetn(&str[0], filesize);
349+
content.append(std::move(str));
350+
}
341351
}
342352
content.append("\r\n");
343353
}

lib/tests/integration_test/client/main.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,23 @@ void doTest(const HttpClientPtr &client, std::shared_ptr<test::Case> TEST_CTX)
728728
CHECK((*json)["P2"] == "test");
729729
});
730730

731+
// Test file upload from memory
732+
auto hello = std::make_shared<std::string>("hello world!");
733+
UploadFile memfile(hello->data(),
734+
hello->length(),
735+
"hello_world.txt",
736+
"hellofile",
737+
ContentType::CT_TEXT_PLAIN);
738+
req = HttpRequest::newFileUploadRequest({memfile});
739+
req->setPath("/api/attachment/uploadMemory");
740+
client->sendRequest(req,
741+
[req, TEST_CTX, hello](ReqResult result,
742+
const HttpResponsePtr &resp) {
743+
REQUIRE(result == ReqResult::Ok);
744+
REQUIRE(resp->contentType() == CT_TEXT_PLAIN);
745+
CHECK(resp->getBody() == *hello);
746+
});
747+
731748
// Test newFileResponse
732749
req = HttpRequest::newHttpRequest();
733750
req->setPath("/RangeTestController/");

lib/tests/integration_test/server/api_Attachment.cc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,32 @@ void Attachment::uploadImage(
103103
callback(resp);
104104
}
105105

106+
void Attachment::uploadMemory(
107+
const HttpRequestPtr &req,
108+
std::function<void(const HttpResponsePtr &)> &&callback)
109+
{
110+
MultiPartParser fileUpload;
111+
112+
if (fileUpload.parse(req) == 0 && fileUpload.getFiles().size() == 1)
113+
{
114+
auto &file = fileUpload.getFiles()[0];
115+
if (file.getItemName() == "hellofile")
116+
{
117+
auto resp = HttpResponse::newHttpResponse();
118+
resp->setStatusCode(HttpStatusCode::k200OK);
119+
resp->setContentTypeCode(ContentType::CT_TEXT_PLAIN);
120+
std::string hello = std::string(file.fileData(), file.fileLength());
121+
resp->setBody(std::move(hello));
122+
callback(resp);
123+
return;
124+
}
125+
}
126+
LOG_DEBUG << "upload text from memory error!";
127+
auto resp = HttpResponse::newHttpResponse();
128+
resp->setStatusCode(HttpStatusCode::k400BadRequest);
129+
callback(resp);
130+
}
131+
106132
void Attachment::download(
107133
const HttpRequestPtr &req,
108134
std::function<void(const HttpResponsePtr &)> &&callback)

lib/tests/integration_test/server/api_Attachment.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class Attachment : public drogon::HttpController<Attachment>
1212
METHOD_ADD(Attachment::get, "", Get); // Path is '/api/attachment'
1313
METHOD_ADD(Attachment::upload, "/upload", Post);
1414
METHOD_ADD(Attachment::uploadImage, "/uploadImage", Post);
15+
METHOD_ADD(Attachment::uploadMemory, "/uploadMemory", Post);
1516
METHOD_ADD(Attachment::download, "/download", Get);
1617
METHOD_LIST_END
1718
// your declaration of processing function maybe like this:
@@ -21,6 +22,8 @@ class Attachment : public drogon::HttpController<Attachment>
2122
std::function<void(const HttpResponsePtr &)> &&callback);
2223
void uploadImage(const HttpRequestPtr &req,
2324
std::function<void(const HttpResponsePtr &)> &&callback);
25+
void uploadMemory(const HttpRequestPtr &req,
26+
std::function<void(const HttpResponsePtr &)> &&callback);
2427
void download(const HttpRequestPtr &req,
2528
std::function<void(const HttpResponsePtr &)> &&callback);
2629
};

0 commit comments

Comments
 (0)