Sooner or later every business application gets the same request: let users download this data, and let them upload that data back in. The next question is the format, and the two candidates are always CSV and Excel. Both work, and both cause real problems when used in the wrong place. A CSV export that opens as a single column in Excel, a phone number that loses its leading zero, a NIK that turns into 3.20123E+15, or an export that exhausts the server memory are all common, and all avoidable. The right choice depends mostly on who or what will read the file.
Three Formats, Not Two
When people say Excel they can mean two different formats, and the difference matters.
| Format | What it is | Limits |
|---|---|---|
| CSV | Plain text, one row per line, values separated by a delimiter. No data types, no formatting, no sheets. | No limit in the format itself. Excel opens only the first 1,048,576 rows. |
| XLSX | The current Excel format since 2007: a ZIP archive of XML files. Keeps data types, formatting, formulas, and multiple sheets. | 1,048,576 rows and 16,384 columns per sheet, 32,767 characters per cell. |
| XLS | The old binary Excel format from Excel 97 to 2003. | 65,536 rows and 256 columns per sheet. |
For new features there is no reason to produce XLS. Every spreadsheet program in use today reads XLSX, and XLS brings tighter limits and older parsing code. When this article says Excel, it means XLSX. One related shortcut is worth avoiding as well: saving a CSV or an HTML table with an .xls extension. Excel opens it, but only after warning that the file format and extension do not match, and many users read that warning as a sign the file is unsafe.
Why CSV Files Look Broken in Excel
Most complaints about CSV are really complaints about how Excel opens CSV files. When a user double-clicks a CSV, Excel guesses the delimiter, the encoding, and the type of every column, and it guesses using the computer regional settings. Several problems follow, and they hit Indonesian users especially often.
- Everything lands in one column. Windows with Indonesian regional settings uses a comma as the decimal separator, so the list separator is a semicolon. A comma-separated CSV then opens with every row stuffed into column A. The same file looks fine on a laptop set to English (United States).
- Leading zeros disappear. Excel treats 081234567890 as a number and shows 81234567890. Postal codes, employee numbers, and product codes that start with zero are affected the same way.
- Long numbers lose digits. Excel keeps only 15 significant digits. A 16-digit NIK or a bank account number appears as 3.20123E+15, and the last digit is replaced with zero. If the user saves the file, the original number is gone.
- Names turn into strange characters. Without a byte order mark, Excel on Windows may read a UTF-8 file in the legacy Windows encoding, so a name like José or a non-Latin script is garbled.
- Dates swap day and month. 04/03/2026 is 4 March in Indonesia and 3 April in the United States, and Excel picks one based on the regional settings.
Writing the CSV carefully helps only partly. Saving as UTF-8 with a byte order mark fixes the encoding, and ISO dates such as 2026-03-04 are read correctly almost everywhere. But nothing in a CSV file can tell Excel that a column is text, so leading zeros and long numbers stay at risk whenever someone double-clicks the file. Users who need to open a CSV in Excel should use Data, then From Text/CSV, where they can pick the delimiter and set columns to text, but most users will not.
What XLSX Fixes, and What It Costs
XLSX stores the type of each cell. A NIK written as text stays text, a date stays a date, and an amount stays a number that can be summed straight away. The file opens the same way on every computer regardless of regional settings, it can hold several sheets, and the header row can be frozen and formatted. For a file that a person will open in Excel, these differences are exactly what the user wants.
The cost is on the server. To see how large it is, we wrote the same 100,000 rows with seven columns (ID, name, NIK, phone number, date, amount, and status) as CSV and as XLSX, then read them back, using Python 3.10 with the standard csv module and openpyxl 3.1.5.
| Measurement | CSV | XLSX, regular mode | XLSX, streaming mode |
|---|---|---|---|
| File size | 7.8 MB | 5.5 MB | 5.5 MB |
| After gzip | 2.5 MB | 5.3 MB | 5.3 MB |
| Time to write | 0.4 s | 7.3 s | 7.1 s |
| Peak memory while writing | 11 MB | 309 MB | 43 MB |
| Time to read all rows | 0.06 s | not measured | 6.3 s |
A few things stand out. An XLSX file is already compressed, so it starts smaller but gains almost nothing from gzip, while CSV ends up half the size once compressed. Writing XLSX took about seventeen times as long as CSV, and reading it back took about a hundred times as long. The biggest difference is memory: in regular mode the library keeps every cell in memory until the file is saved, so memory grows with the size of the export. The same library in streaming mode writes rows out as it goes and used about a seventh of the memory. The memory figures include around 10 MB for the Python interpreter itself.
Other languages show the same pattern. Most libraries have a convenient mode that builds the whole workbook in memory and a streaming mode for large files: Laravel Excel and PhpSpreadsheet in PHP with chunked queries and queued exports, or OpenSpout for pure streaming, ExcelJS with its streaming WorkbookWriter in Node.js, and excelize with its StreamWriter in Go. One warning for Node.js projects: the xlsx package on npm, from SheetJS, has stayed at version 0.18.5 with known vulnerabilities, including prototype pollution and ReDoS. Newer versions are published only on the SheetJS CDN, so check which one your project actually installs.
Which One to Use
The question that decides it is who reads the file next. A person working in Excel needs XLSX. Another program needs CSV, or better, an API.
| Situation | Recommended format | Why |
|---|---|---|
| Reports downloaded by finance, admin, or management | XLSX | Opens correctly on any computer, keeps numbers summable and codes intact |
| Data sent to another system, a data warehouse, or a script | CSV in UTF-8 | Simple to produce and parse, streams easily, every tool supports it |
| Bulk upload files whose format is set by a bank, marketplace, or government portal | Whatever the specification says | Many require CSV or TXT with an exact delimiter and column order, and the spec always wins |
| Exports larger than about a million rows | CSV, compressed as ZIP or gzip | Beyond the XLSX row limit, and much cheaper to generate |
| Several related tables in one download | XLSX with one sheet per table | CSV can only hold one table per file |
| Data filled in by users and uploaded back | An XLSX template, with CSV also accepted | The template can mark columns as text and add drop-down lists |
| Two systems exchanging data every day | An API, or CSV on a schedule if an API is not possible | Files sent by hand are easy to forget, edit, or send twice |
Offering both is often the right answer for exports. A report page with a Download Excel button for people and a Download CSV button for analysts and integrations costs little extra, because both are produced from the same query.
Building an Export That Holds Up
- Use streaming mode from the start, and read the data from the database in chunks or with a cursor instead of loading everything with a single query.
- Generate large exports in a background job, then notify the user or show a download link when the file is ready. An HTTP request that runs for minutes is cut off by the proxy or the load balancer sooner or later.
- In XLSX, write NIK, phone numbers, account numbers, and other codes as text cells, write amounts as numbers, and write dates as real dates.
- In CSV, use UTF-8 with a byte order mark if the file will be opened in Excel, quote values according to RFC 4180, and use ISO 8601 dates.
- Protect against formula injection. A cell whose value starts with =, +, -, or @ can run as a formula when the file is opened. OWASP recommends prefixing such values with a single quote, which matters whenever an export contains text that users typed, such as names, addresses, or notes.
- Apply the same access rules as the screen the export comes from. An export that skips the permission filter is one of the most common ways data leaks out of business applications.
Building an Import That Does Not Break on Real Files
Imports are harder than exports, because the file comes from a user and can contain anything. A CSV saved by Excel on an Indonesian computer will often use semicolons, and unless the user chose CSV UTF-8 when saving, it will be in the legacy Windows encoding. An import feature has to expect that.
- 1Provide a template to download, preferably XLSX, with the expected headers, code columns already set to text, and drop-down lists for values such as status or category.
- 2If you accept CSV, detect the delimiter (comma, semicolon, or tab) and the encoding instead of assuming them, and match columns by header name rather than position.
- 3Set limits before parsing: maximum file size, maximum number of rows, and only the extensions you support. An XLSX file is a ZIP archive, so a small upload can expand into a very large file. Use a library that limits this, and reject .xlsm files that contain macros.
- 4Read the stored cell values and never evaluate formulas from an uploaded file.
- 5Validate every row and show a preview with the errors per row, for example row 37: NIK must be 16 digits, before anything is saved.
- 6Save everything in one transaction, or through a staging table, so a failure in the middle does not leave half the data imported. For large files, process the import in a background job and report the result when it is done.
In Short
CSV is the better format for machines and XLSX is the better format for people. Most problems come from using a CSV where people will open it by double-clicking, or from producing a large XLSX without streaming. Decide based on who reads the file next, use streaming and background jobs for large exports, and treat every uploaded file as untrusted input. If your application needs import and export features built or fixed, especially for large data or for integration with other systems, our custom software team can help.
Key takeaways
- Use XLSX for files people will open in Excel, and CSV for files read by other systems, scripts, or very large exports. Do not produce the old XLS format.
- Excel opens CSV using regional settings, so Indonesian users often see one column, lost leading zeros, NIKs in scientific notation, and garbled names.
- In our test with 100,000 rows, XLSX took about 17 times longer to write than CSV, and regular mode used 309 MB of memory compared with 43 MB in streaming mode.
- For exports, stream the data, run large jobs in the background, write codes as text, and guard against formula injection.
- For imports, provide an XLSX template, detect delimiter and encoding, limit file size, validate every row with a preview, and save in a single transaction.


