从 base64 解码并保存图像文件的 C++ 实现

4
我正在尝试用c++编写程序,能够将图像编码为base64,并将base64解码为图像。我相信编码函数运行良好,一些网站可以接受我生成的base64代码并将其解码为图像。但是,因为某种原因,一旦我将base64解码为字符串并将其写入文件,保存为png格式,就会显示无法在图像浏览器中打开该文件。
我确认被写入新文件的字符串与现有文件完全相同(在文本编辑器中打开时),但出于某种原因,新文件无法打开,而现有文件可以。我甚至尝试过仅在文本编辑器中创建新文件,然后将老文件中的文本复制到其中,但仍无法在图像浏览器中打开。
我认为编码函数和base64解码函数都运行良好,问题可能出在图像解码函数中。
图像编码函数
string base64_encode_image(const string& path) {
    vector<char> temp;

    std::ifstream infile;
    infile.open(path, ios::binary);     // Open file in binary mode
    if (infile.is_open()) {
        while (!infile.eof()) {
            char c = (char)infile.get();
            temp.push_back(c);
        }
        infile.close();
    }
    else return "File could not be opened";
    string ret(temp.begin(), temp.end() - 1);
    ret = base64_encode((unsigned const char*)ret.c_str(), ret.size());

    return ret;
}

图像解码功能

void base64_decode_image(const string& input) {
    ofstream outfile;
    outfile.open("test.png", ofstream::out);

    string temp = base64_decode(input);

    outfile.write(temp.c_str(), temp.size());
    outfile.close();
    cout << "file saved" << endl;
}

编码函数 Base64

string base64_encode(unsigned const char* input, unsigned const int len) {
    string ret;
    size_t i = 0;
    unsigned char bytes[3];
    unsigned char sextets[4];

    while (i <= (len - 3)) {
        bytes[0] = *(input++);
        bytes[1] = *(input++);
        bytes[2] = *(input++);

        sextets[0] = (bytes[0] & 0xfc) >> 2;    // Cuts last two bits off of first byte
        sextets[1] = ((bytes[0] & 0x03) << 4) + ((bytes[1] & 0xf0) >> 4);   // Takes last two bits from first byte and adds it to first 4 bits of 2nd byte
        sextets[2] = ((bytes[1] & 0x0f) << 2) + ((bytes[2] & 0xc0) >> 6);   // Takes last 4 bits of 2nd byte and adds it to first 2 bits of third byte
        sextets[3] = bytes[2] & 0x3f;   // takes last 6 bits of third byte

        for (size_t j = 0; j < 4; ++j) {
            ret += base64_chars[sextets[j]];
        }

        i += 3; // increases to go to third byte
    }

    if (i != len) {
        size_t k = 0;
        size_t j = len - i; // Find index of last byte
        while (k < j) {     // Sets first bytes
            bytes[k] = *(input++);
            ++k;
        }

        while (j < 3) {     // Set last bytes to 0x00
            bytes[j] = '\0';
            ++j;
        }

        sextets[0] = (bytes[0] & 0xfc) >> 2;    // Cuts last two bits off of first byte
        sextets[1] = ((bytes[0] & 0x03) << 4) + ((bytes[1] & 0xf0) >> 4);   // Takes last two bits from first byte and adds it to first 4 bits of 2nd byte
        sextets[2] = ((bytes[1] & 0x0f) << 2) + ((bytes[2] & 0xc0) >> 6);   // Takes last 4 bits of 2nd byte and adds it to first 2 bits of third byte
        // No last one is needed, because if there were 4, then (i == len) == true

        for (j = 0; j < (len - i) + 1; ++j) {   // Gets sextets that include data
            ret += base64_chars[sextets[j]];    // Appends them to string
        }

        while ((j++) < 4)   // Appends remaining ='s
            ret += '=';

    }

    return ret;

}

解码base64函数

string base64_decode(const string& input) {
    string ret;
    size_t i = 0;
    unsigned char bytes[3];
    unsigned char sextets[4];

    while (i < input.size() && input[i] != '=') {
        size_t j = i % 4;   // index per sextet
        if (is_base64(input[i])) sextets[j] = input[i++];       // set sextets with characters from string
        else { cerr << "Non base64 string included in input (possibly newline)" << endl; return ""; }
        if (i % 4 == 0) {
            for (j = 0; j < 4; ++j)             // Using j as a seperate index (not the same as it was originally used as, will later be reset)
                sextets[j] = indexof(base64_chars, strlen(base64_chars), sextets[j]);   // Change value to indicies of b64 characters and not ascii characters

            bytes[0] = (sextets[0] << 2) + ((sextets[1] & 0x30) >> 4);  // Similar bitshifting to before
            bytes[1] = ((sextets[1] & 0x0f) << 4) + ((sextets[2] & 0x3c) >> 2);
            bytes[2] = ((sextets[2] & 0x03) << 6) + sextets[3];

            for (j = 0; j < 3; ++j)     // Using j seperately again to iterate through bytes and adding them to full string
                ret += bytes[j];
        }
    }

    if (i % 4 != 0) {
        for (size_t j = 0; j < (i % 4); ++j)
            sextets[j] = indexof(base64_chars, strlen(base64_chars), sextets[j]);

        bytes[0] = (sextets[0] << 2) + ((sextets[1] & 0x30) >> 4);  // Similar bitshifting to before
        bytes[1] = ((sextets[1] & 0x0f) << 4) + ((sextets[2] & 0x3c) >> 2);
        for (size_t j = 0; j < (i % 4) - 1; ++j)
            ret += bytes[j];        // Add final bytes
    }
    return ret;
}

当我尝试打开由图像解码函数生成的文件时,它会显示文件格式不受支持或已损坏。 我正在尝试解码的base64编码函数在此链接中: https://pastebin.com/S5D90Fs8

小问题,但是如果len小于3,则while (i <= (len - 3))会失败。while (i + 3 <= len)更好。 - john
在将temp传递给base64编码器之前,为什么要将其复制到字符串中的base64_encode_image函数中? - 1201ProgramAlarm
我看到你的代码读取了一个多余的字符,然后从输入字符串中删除了该额外字符。一开始读取正确数量的字符会更简单。像这样 char c; while (infile.get(c)) {。现在你不需要在结尾处使用 string ret(temp.begin(), temp.end() - 1);更多信息 - john
1个回答

5
当你在base64_decode_image中打开outfile时,没有像在读取图像时在base64_encode_image中指定ofstream::binary标志。没有这个标志,你正在文本模式下写入,这可能会改变你正在写入的数据(当调整换行符时)。

这个立刻就起作用了。谢谢您的回复。 - Rory Hemmings

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接