New Requirement
Recently, a new requirement emerged where previously downloadable PDF and Excel files now need to support inline preview functionality instead of direct downloads.
The frontend team encountered challenges when reviewing existing code. Previously, both download and preview endpoints returned binary streams direct to the frontend. Even dynamically generated documents followed the same pattern, writing binary data into HTTP responses. The issue was that browsers would automatically initiate downloads rather than allowing further processing.
After consulting with backend developers, a solution was proposed involving encoding files as Base64 strings before transmission.
Solution Approach
Research led to a viable method for converting files to Base64 format using the approach detailed at:
https://blog.csdn.net/dbeautifullife/article/details/77650713
A utility class was created based on this technique, enhanced with methods for generating dynamic PDFs and Excel documents.
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.context.annotation.Scope;
import sun.misc.BASE64Encoder;
import java.io.*;
/**
* @Description: File to Base64 conversion utility
* @ClassName: Base64Converter
* @author: dengchao
* @date: 2020/11/24 9:27
*/
@Slf4j
@Scope(value = "singleton")
public class Base64Converter {
private Base64Converter() {}
private static final BASE64Encoder encoder = new BASE64Encoder();
public static String convertPdfToBase64(File file) {
if(file == null || !file.exists()){
throw new RuntimeException("File object is null or file does not exist");
}
if(file.isDirectory()){
throw new RuntimeException("Provided file object is a directory");
}
log.error("convertPdfToBase64 - Starting to read file!");
try (FileInputStream fin = new FileInputStream(file);
BufferedInputStream bin = new BufferedInputStream(fin);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedOutputStream bout = new BufferedOutputStream(baos);
) {
byte[] buffer = new byte[1024];
int length = bin.read(buffer);
while(length != -1) {
bout.write(buffer, 0, length);
length = bin.read(buffer);
}
bout.flush();
return byteArrayToBase64(baos);
} catch (FileNotFoundException e) {
log.error("convertPdfToBase64 - File not found!");
e.printStackTrace();
} catch (IOException e) {
log.error("convertPdfToBase64 - Error reading file!");
e.printStackTrace();
}
return "";
}
public static String convertWorkbookToBase64(Workbook workbook) {
if(workbook == null) {
throw new RuntimeException("Excel workbook object is invalid");
}
String result = "";
try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
workbook.write(outputStream);
outputStream.flush();
result = byteArrayToBase64(outputStream);
} catch(Exception e) {
result = "";
log.error("Error converting Excel to Base64", e);
}
return result;
}
public static String byteArrayToBase64(ByteArrayOutputStream outputStream) {
if(outputStream == null) {
throw new RuntimeException("Invalid byte array output stream");
} else {
byte[] bytes = outputStream.toByteArray();
return encoder.encodeBuffer(bytes).trim();
}
}
}
The core concept involves transforming binary streams in to Base64 encoded strings.
Implementation Details
After implementing the utility class, integration testing with frontend colleagues confirmed successful Base64 string generation. Frontend developers could then process these strings appropriately.
The frontend implementation used Vue components to handle Excel file rendering:
const workbook = await XLSX.read(res.responseBody, { type: 'base64', cellDates: true });
workbook.SheetNames.map(async item => {
if (item) {
const title = item;
let html = await XLSX.utils.sheet_to_html(workbook.Sheets[item]).match(//)[0];
const totalTr = html.match(/</tr>/g).length;
const totalEmptyTd = html.match(/<td></td></tr>/g).length;
if (totalTr === totalEmptyTd) {
html = html.replace(/<td></td></tr>/g, '</tr>');
}
this.xlsxHtml.push({title, html});
}
});
Similar handling was applied to PDF files. While this approach meets basic requirements for inline preview functionality, it has security limitations. Since the complete file data can be reconstructed from Base64 strings, this method may not satisfy strict security standards for sensitive applications.