drogonframework / drogon

Drogon: A C++14/17/20 based HTTP web application framework running on Linux/macOS/Unix/Windows
MIT License
11.44k stars 1.1k forks source link

how to response image #1695

Closed kyushu closed 1 year ago

kyushu commented 1 year ago

Hi, i want to build a image file server, but i don't know how to response image

i have tried the following code in controller but it does not work

int width, height, channels;
unsigned char *img = stbi_load("test_result/images/a11/test_01.jpg", &width, &height, &channels, 0);
std::string body = drogon::utils::base64Encode(img, width * height * channels);

auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k200OK);
resp->setContentTypeCode(CT_IMAGE_JPG);
 resp->setBody(body);
callback(resp);

could you write an example for me?

VladlenPopolitov commented 1 year ago

You should not make base64 encode for image. You have to send it as is (it is requirements if HTTP protocol). Code looks correct, except you need assign img to body directly: std::string body(static_cast<char*>(img), length_of_img_data ); You need only to provide the length of data. strlen should not be used, as img data can contain \0 char inside. By the way, drogon has build in function to download jpeg file, if they are in root folder (root folder assigned in settings file).

kyushu commented 1 year ago

You should not make base64 encode for image. You have to send it as is (it is requirements if HTTP protocol). Code looks correct, except you need assign img to body directly: std::string body(static_cast<char*>(img), length_of_img_data ); You need only to provide the length of data. strlen should not be used, as img data can contain \0 char inside. By the way, drogon has build in function to download jpeg file, if they are in root folder (root folder assigned in settings file).

Thanks for response so quick

I've tried your way to set body but still can't get correct result, i post my controller header/cpp and the error message on web browser

could you point me out what is wrong ? Thanks !

in header

#pragma once
#include <drogon/HttpController.h>
using namespace drogon;
class ImageCtrl : public drogon::HttpController<ImageCtrl>
{
  public:
    METHOD_LIST_BEGIN
    ADD_METHOD_VIA_REGEX(ImageCtrl::handle_image, "/images/.*", Get);
    METHOD_LIST_END
    void handle_image(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) const;
};

in cpp

void ImageCtrl::handle_image(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) const
{
    std::string path = req->getPath();
    std::cout << "path: " << path << std::endl;

    int width, height, channels;
    unsigned char *img = stbi_load("/test_result/images/a11/test_01.jpg", &width, &height, &channels, 0);
    std::string body(reinterpret_cast<char*>(img), width*height*channels);

    auto resp = HttpResponse::newHttpResponse();
    resp->setStatusCode(k200OK);
    resp->setContentTypeCode(CT_IMAGE_JPG);
    resp->setBody(body);

    callback(resp);
}

in web browser it shows : The image "http://localhost/images/" cannot be displayed because it contains errors.

VladlenPopolitov commented 1 year ago

Your Drogon controller is correct, it sends data to browser, the browser reads them, understands, that it is image , but does no understand image format. The problem is with data. If you send JPEG, you have to read JPEG file (fstream open(), read(), close()) and send these data to controller in a body. If you load image, you sends not JPEG, you send pixels as data.

hwc0919 commented 1 year ago

Try use HttpResponse::newFileResponse(). Search this api in source code and you will find some example codes.

kyushu commented 1 year ago

Try use HttpResponse::newFileResponse(). Search this api in source code and you will find some example codes.

@VladlenPopolitov , @hwc0919 Thanks !

I use HttpResponse::newFileResponse() and it works

In case anyone who is same as me new to web backend development, here is my summary:

  1. If you want to response with image, you need to put all binary data of file in the body of response and set correspond content type like @VladlenPopolitov says, i was wrong for putting pixel data in the body of response.
  2. Drogon has built-in FileResponse can do this for you.

for my case, here is an example code:

std::string file_path = "/test_result/images/a11/test_01.jpg";
auto resp = HttpResponse::newFileResponse(file_path, "", CT_IMAGE_JPG);