Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Dynamic CSV Export for Large Datasets Using LinkedHashMap in Java

Tech 1

CSV Export Utility

The DynamicCsvExporter class handles writing CSV content directly from query result maps without requiring predefined DTOs. It relies on LinkedHashMap to preserve column ordering and retain null entries. The MyBatis mapper returns java.util.LinkedHashMap (resultType).

package com.example.util;

import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class DynamicCsvExporter {

    private static final String DELIMITER = ",";
    private static final String LINE_END = "\r\n";

    /**
     * Writes CSV content to the output stream.
     *
     * @param records data rows as LinkedHashMap list
     * @param headers comma-separated header names
     * @param fields  comma-separated map keys
     * @param out     target output stream
     */
    public static void writeCsv(List<LinkedHashMap<String, String>> records,
                                String headers, String fields, OutputStream out) throws Exception {
        StringBuilder content = new StringBuilder();
        String[] headerArr = headers.split(",");
        String[] fieldArr = fields.split(",");

        // Append header row
        for (String h : headerArr) {
            content.append(h).append(DELIMITER);
        }
        content.append(LINE_END);

        // Append data rows
        if (!CollectionUtils.isEmpty(records)) {
            for (LinkedHashMap<String, String> row : records) {
                for (String field : fieldArr) {
                    content.append(escapeCell(row.get(field))).append(DELIMITER);
                }
                content.append(LINE_END);
            }
        }

        out.write(content.toString().getBytes("UTF-8"));
        out.flush();
    }

    /**
     * Configures HTTP response for CSV download.
     *
     * @param baseFileName file name without extension
     * @param response     HttpServletResponse
     */
    public static void prepareResponse(String baseFileName, HttpServletResponse response)
            throws UnsupportedEncodingException {
        String timestamp = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
        String fullName = baseFileName + timestamp + ".csv";
        String charset = "UTF-8";

        response.setContentType("application/ms-txt.numberformat:@");
        response.setCharacterEncoding(charset);
        response.setHeader("Pragma", "public");
        response.setHeader("Cache-Control", "max-age=30");
        response.setHeader("Content-Disposition", "attachment; filename="
                + URLEncoder.encode(fullName, charset));
    }

    private static String escapeCell(Object value) {
        if (value == null) {
            return "";
        }
        String cell = value.toString();
        if (cell.contains("\"")) {
            cell = cell.replace("\"", "\"\"");
        }
        if (cell.contains(",")) {
            return "\"" + cell + "\"";
        }
        // prefix with tab to prevent date-format auto-conversion in some spreadsheet apps
        return "\t" + cell;
    }
}

MyBatis Configuration

Enable null-value mapping in MyBatis (or MyBatis-Plus) to ensure columns with null values appear in the result map:

# mybatis-plus example
mybatis-plus:
  configuration:
    call-setters-on-nulls: true

# plain mybatis example
mybatis:
  configuration:
    call-setters-on-nulls: true

Mapper XML should define resultType="java.util.LinkedHashMap" for the query that fetches export data.

Controller Endpoint

A Spring MVC controller method accepts filter criteria, queries the data base, dynamically derives column keys from the first row, and streams the CSV response.

@PostMapping(value = "/export-csv", produces = "text/plain;charset=UTF-8")
public void exportCsv(HttpServletResponse response, @RequestBody Map<String, Object> params) throws Exception {
    List<LinkedHashMap<String, String>> rows = reportMapper.fetchExportData(params);
    ServletOutputStream out = response.getOutputStream();

    // Build comma-separated key list from first row
    String keys = buildKeys(rows.get(0));
    // Headers are identical to keys in this example (or can be separate mapping)
    // Set response headers
    DynamicCsvExporter.prepareResponse("report", response);
    DynamicCsvExporter.writeCsv(rows, keys, keys, out);
}

private String buildKeys(LinkedHashMap<String, String> sample) {
    StringBuilder sb = new StringBuilder();
    for (String key : sample.keySet()) {
        sb.append(key).append(",");
    }
    // Remove trailing comma
    return sb.substring(0, sb.length() - 1);
}

Client-Side Dowlnoad

The frontend performs an HTTP POST, converts the response into a Blob, and triggers a file download.

this.$http
    .post("/api/report/export-csv", {
        startDate: this.filters.startDate,
        endDate: this.filters.endDate
    })
    .then((res) => {
        loading.close();
        const blob = new Blob([res.data]);
        const url = window.URL.createObjectURL(blob);
        const anchor = document.createElement('a');
        anchor.href = url;
        anchor.download = 'export-' + Date.now() + '.csv';
        document.body.appendChild(anchor);
        anchor.click();
        document.body.removeChild(anchor);
        window.URL.revokeObjectURL(url);
    })
    .catch((e) => {
        loading.close();
        this.$message.error(e.message);
    });
Tags: Java

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.