I'm trying to write a program in c++ that can encode images into base64 and also decode base64 into images. I believe the encoder function is working fine and some websites can take the base64 code I generate and decode it into the image fine, but for some reason once I decode the base64 into a string and then write it to a file and save it as a png it says it can't be opened in an image viewer.
I confirmed that the string that is being written to the new file is exactly the same as the existing file (when opened in a text editor), but for some reason, the new file can't be opened but the existing one can be. I have even tried just making a new file in a text editor, and copying the text from the old file into it, but it still doesn't open in an image viewer.
I believe that both of the encode functions and the base64 decode function all work fine. I think the problem is in the Image Decode function.
Image Encode Function
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;
}
Image Decode Function
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;
}
Encode Function 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;
}
Decode Function 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;
}
When I try to open the files produced by Image decode function It says that the file format isn't supported, or that it has been corrupted. The base64 produced by the encode function that I'm trying to decode is in this link https://pastebin.com/S5D90Fs8
When you open outfile
in base64_decode_image
, you do not specify the ofstream::binary
flag like you do in base64_encode_image
when reading the image. Without that flag, you're writing in text mode which can alter the data you're writing (when adjusting for newlines).