Loading learning content...
One of the most common causes of corrupted file transfers in FTP is selecting the wrong transfer mode. A single incorrect setting can render an executable unusable, corrupt an image beyond recognition, or subtly mangle a text file's formatting. Understanding the difference between ASCII and Binary modes—and when to use each—is essential knowledge for anyone working with FTP.
This distinction exists because of a fundamental truth in computing: not all files are created equal. Some files are designed to be human-readable text, while others are sequences of binary data with specific byte values that must remain exactly as they are. FTP's transfer modes address this duality.
By the end of this page, you will understand exactly what ASCII and Binary modes do, how transfer type conversions affect file contents, when to use each mode, common corruption scenarios, and strategies for mode selection in automated systems.
FTP transfer mode is set using the TYPE command, which configures how the server and client will encode data during transfer.
Command Syntax:
TYPE <SP> <type-code> [<SP> <format-control>] <CRLF>
The type-code specifies the fundamental data representation, and optional format-control specifies additional encoding details.
| Type Code | Name | Purpose | Usage |
|---|---|---|---|
| A | ASCII | Text files with line ending conversion | Most common for text |
| E | EBCDIC | IBM mainframe text encoding | Legacy mainframe systems |
| I | Image/Binary | Exact byte-for-byte transfer | Most common overall |
| L <n> | Local | Local byte size specification | Rare; specialized uses |
1234567891011121314151617181920212223242526272829
# Setting ASCII mode→ TYPE A← 200 Switching to ASCII mode # Setting Binary mode (Image)→ TYPE I← 200 Switching to Binary mode # ASCII with format control (rarely used)→ TYPE A N # Non-print (default)← 200 Switching to ASCII Non-print mode → TYPE A T # Telnet format effectors← 200 Switching to ASCII Telnet mode → TYPE A C # Carriage control← 200 Switching to ASCII Carriage control mode # EBCDIC mode (mainframe systems)→ TYPE E← 200 Switching to EBCDIC mode # Local byte size (8-bit bytes)→ TYPE L 8← 200 Switching to Local mode with byte size 8 # Error: Invalid type→ TYPE X← 504 Command not implemented for that parameterPractical Usage:
In modern practice, only two modes are commonly used:
EBCDIC and Local modes are legacy features that exist for compatibility with mainframe systems but are rarely encountered in everyday use.
FTP servers typically default to ASCII mode after login. However, never assume—always explicitly set the mode with TYPE before transferring files. A surprising number of transfer bugs stem from incorrect assumptions about the current mode.
ASCII mode (TYPE A) is designed for transferring text files between systems that may use different conventions for representing text. The primary purpose is line ending conversion—transforming line breaks to match the conventions of the receiving system.
The Line Ending Problem:
Different operating systems historically used different characters to mark the end of a line in text files:
| Platform | Line Ending | Hex Values | Common Name |
|---|---|---|---|
| Unix/Linux/macOS | LF ( ) | 0x0A | Newline / Line Feed |
| Windows/DOS | CRLF (\r ) | 0x0D 0x0A | Carriage Return + Line Feed |
| Classic Mac (pre-X) | CR (\r) | 0x0D | Carriage Return |
| FTP Network | CRLF (\r ) | 0x0D 0x0A | Network Virtual Terminal ASCII |
How ASCII Mode Works:
When transferring in ASCII mode, FTP performs line ending conversion in two steps:
Sending (Client/Server → Network):
Receiving (Network → Client/Server):
Example Transfer:
1234567891011121314151617181920212223242526272829303132333435
# Original text file content (on Unix server):# Bytes: "HelloWorld"# Hex: 48 65 6C 6C 6F 0A 57 6F 72 6C 64 0A # Step 1: Unix client uploads to server# Client converts LF → CRLF for network transmission:# Network bytes: "Hello\rWorld\r"# Hex: 48 65 6C 6C 6F 0D 0A 57 6F 72 6C 64 0D 0A # Step 2: Windows client downloads same file# Server sends CRLF format (standard network format)# Network bytes: "Hello\rWorld\r" # Step 3: Windows client receives and converts# CRLF is already Windows native, so no conversion needed:# Local file: "Hello\rWorld\r"# Hex: 48 65 6C 6C 6F 0D 0A 57 6F 72 6C 64 0D 0A # Step 4: Unix client downloads same file# Receives CRLF from network# Converts CRLF → LF for Unix:# Local file: "HelloWorld"# Hex: 48 65 6C 6C 6F 0A 57 6F 72 6C 64 0A # Result: Text displays correctly on all platforms!ASCII mode only converts line endings—it does NOT perform character encoding conversion (like UTF-8 to UTF-16). Modern text files with non-ASCII characters should generally be transferred in binary mode to preserve exact byte sequences. ASCII mode works best for simple 7-bit ASCII text files.
Binary mode (TYPE I, also called Image mode) transfers files as exact byte sequences with no modifications whatsoever. Every byte sent equals every byte received, preserving the precise binary structure of the file.
Why "Image" Mode?
The name "Image" comes from early computing where binary data was often referred to as a memory or disk "image"—an exact replica of the original data. The TYPE I command captures this concept of creating a perfect copy.
What Binary Mode Guarantees:
123456789101112131415161718192021222324252627282930313233343536373839
# Example: Transferring a PNG image in binary mode # Original file: logo.png (1,234 bytes)# First 8 bytes (PNG signature): 89 50 4E 47 0D 0A 1A 0A → TYPE I← 200 Switching to Binary mode → SIZE logo.png← 213 1234 → PASV← 227 Entering Passive Mode (...) → RETR logo.png← 150 Opening BINARY mode data connection for logo.png (1234 bytes) # Data channel transfers exactly 1,234 bytes# Including bytes 0x0D 0x0A which would be modified in ASCII mode! ← 226 Transfer complete # Destination file verification:# Size: 1,234 bytes ✓# First 8 bytes: 89 50 4E 47 0D 0A 1A 0A ✓# MD5: matches source ✓ # ============================================# Critical: If this were transferred in ASCII mode: → TYPE A← 200 Switching to ASCII mode→ RETR logo.png← 150 Opening ASCII mode data connection # ASCII mode sees 0D 0A (CRLF) in PNG signature!# Converts to local format (on Unix: just 0A)# PNG signature becomes: 89 50 4E 47 0A 1A 0A# File is CORRUPTED - PNG readers won't recognize itBinary Mode is Safe for Everything
Binary mode never corrupts files. The worst that can happen is that text files end up with "wrong" line endings for the target platform—but the file remains valid and can be converted afterward.
ASCII mode, on the other hand, will corrupt binary files by misinterpreting byte sequences as line endings.
This asymmetry leads to a practical rule: When in doubt, use binary mode.
Many binary file formats (PNG, ZIP, PDF, EXE) contain the byte sequence 0x0D 0x0A somewhere in their data. In ASCII mode, this sequence is interpreted as a line ending and may be converted, corrupting the file. Always use binary mode for non-text files.
Understanding exactly how files get corrupted helps diagnose transfer problems and reinforces why mode selection matters. Let's examine specific corruption scenarios in detail.
Scenario 1: Binary File Transferred in ASCII Mode
This is the most common and severe corruption case:
1234567891011121314151617181920212223242526272829303132333435363738
# Original JPEG file partial hex dump:# FF D8 FF E0 00 10 4A 46 49 46 00 01 01 00 ...# ... somewhere in the file: ... 0D 0A ... (coincidental byte sequence)# ... file continues with more image data ... # Size: 2,048,000 bytes # INCORRECTLY transferred in ASCII mode from Windows server to Unix client: # Transfer process:# 1. Server reads file, finds 0D 0A sequences# 2. Server may or may not modify (depending on implementation)# 3. Unix client receives, converts 0D 0A → 0A (removes 0D bytes) # Result on Unix client:# - Size: 2,047,847 bytes (153 bytes SMALLER)# - Every 0D 0A became just 0A# - File is CORRUPT:# - Image viewers show nothing or partial garbage# - JPEG structure is broken# - Cannot be repaired without re-transferring correctly # ============================================# Scenario 2: EXE file transferred in ASCII mode # Original Windows executable: setup.exe# Size: 15,728,640 bytes# Contains countless 0D 0A sequences in code/data sections # Transferred in ASCII mode to Unix and back to Windows:# - Unix conversion: 0D 0A → 0A (bytes removed)# - Return trip: 0A → 0D 0A (bytes added, but in DIFFERENT places!) # Result:# - Size: somewhere around 15.7MB but NOT exactly right# - Executable is completely broken# - Windows shows: "This app can't run on your PC"# - Antivirus may flag as corrupted/suspiciousScenario 2: Text File Transferred in Binary Mode
This scenario is less severe—files remain valid but may display incorrectly:
123456789101112131415161718192021222324252627282930313233343536373839404142
# Original Unix text file (readme.txt):"Line 1Line 2Line 3"# Hex: 4C 69 6E 65 20 31 0A 4C 69 6E 65 20 32 0A 4C 69 6E 65 20 33 0A # Transferred in BINARY mode to Windows:# File arrives with exact bytes (no conversion) # Result when opened in Windows Notepad (old version):"Line 1⌂Line 2⌂Line 3⌂"# Appears as one line with strange characters# The 0A bytes aren't recognized as line endings # Modern text editors (VS Code, Notepad++) handle it correctly:Line 1Line 2 Line 3# They auto-detect Unix line endings # ============================================# Opposite case: Windows file to Unix # Windows text file:"Line 1\rLine 2\r"# Hex: ... 0D 0A ... 0D 0A # Binary transfer to Unix:# File has CRLF line endings # Result in Unix editors:Line 1^MLine 2^M# The ^M represents the visible carriage return character# Most Unix tools handle this, but some scripts may fail # Easy fix: dos2unix command$ dos2unix readme.txtdos2unix: converting file readme.txt to Unix format...Wrong mode for binary files = permanent corruption. Wrong mode for text files = minor inconvenience. This is why 'default to binary mode' is such a universal recommendation. The downside of binary mode for text is trivially fixable; the downside of ASCII mode for binary is catastrophic.
Choosing the correct transfer mode requires identifying whether a file is text or binary. Here's a comprehensive guide to making that determination.
By File Extension:
File extensions provide a quick heuristic, though not foolproof:
| Category | Extensions | Mode | Notes |
|---|---|---|---|
| Plain Text | .txt, .csv, .log, .md | ASCII or Binary | ASCII for cross-platform; Binary safer |
| Source Code | .c, .h, .py, .js, .java, .go, .rs | ASCII or Binary | ASCII if sharing across platforms |
| Config/Data | .json, .xml, .yaml, .toml, .ini | ASCII or Binary | ASCII preserves readability |
| Web | .html, .htm, .css | ASCII or Binary | UTF-8 files prefer Binary |
| Shell Scripts | .sh, .bash, .zsh, .bat, .ps1 | ASCII | Line endings critical for execution |
| Executables | .exe, .dll, .so, .dylib, .bin | BINARY | Always binary; corruption is fatal |
| Archives | .zip, .tar, .gz, .7z, .rar, .bz2 | BINARY | Compressed data is binary |
| Images | .jpg, .png, .gif, .bmp, .ico, .svg | BINARY | Even SVG (XML) should be binary to preserve encoding |
| Audio/Video | .mp3, .wav, .mp4, .avi, .mkv | BINARY | Media files are always binary |
| Documents | .pdf, .doc, .docx, .xls, .ppt | BINARY | Office formats are binary/compressed |
| Fonts | .ttf, .otf, .woff, .woff2 | BINARY | Font data is binary |
| Databases | .db, .sqlite, .mdb | BINARY | Database files are binary |
By Content Inspection:
For unknown files, content inspection provides more accuracy:
Magic Bytes: Many file formats have identifying byte sequences at the start:
89 50 4E 47 (\x89PNG)25 50 44 46 (%PDF)50 4B 03 04 (PK..)4D 5A (MZ)Null Byte Check: Text files rarely contain null bytes (0x00). Presence of nulls strongly indicates binary.
High-Byte Ratio: Files with many bytes > 127 are likely binary (or non-ASCII text like UTF-16).
MIME Type: System MIME type detection (file command on Unix) provides reliable identification.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
import mimetypesfrom pathlib import Path def should_use_binary_mode(filepath: str) -> bool: """ Determine if file should be transferred in binary mode. Returns True for binary mode, False for ASCII mode. Conservative approach: default to binary when uncertain. """ path = Path(filepath) # Check by MIME type mime_type, encoding = mimetypes.guess_type(str(path)) if mime_type: # Text types that could use ASCII mode text_types = [ 'text/', # text/plain, text/html, etc. 'application/json', 'application/xml', 'application/javascript', ] for text_type in text_types: if mime_type.startswith(text_type): # Even for text, there are exceptions if path.suffix.lower() in ['.svg']: return True # SVG may have encoding issues return False # Could use ASCII mode # Everything else is binary return True # Unknown MIME type - inspect content try: with open(path, 'rb') as f: chunk = f.read(8192) # Check for null bytes (strong binary indicator) if b'\x00' in chunk: return True # Check ratio of non-printable characters non_printable = sum( 1 for b in chunk if b < 32 and b not in (9, 10, 13) # Allow tab, LF, CR ) if non_printable > len(chunk) * 0.1: # >10% non-printable return True # Appears to be text return False except Exception: # When in doubt, binary is safer return True # Extension-based quick lookupBINARY_EXTENSIONS = { '.exe', '.dll', '.so', '.dylib', '.bin', '.zip', '.tar', '.gz', '.bz2', '.7z', '.rar', '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.ico', '.webp', '.mp3', '.wav', '.ogg', '.flac', '.aac', '.mp4', '.avi', '.mkv', '.mov', '.webm', '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.ttf', '.otf', '.woff', '.woff2', '.db', '.sqlite', '.mdb', '.pyc', '.class', '.o', '.obj',} TEXT_EXTENSIONS = { '.txt', '.log', '.csv', '.c', '.h', '.cpp', '.hpp', '.py', '.js', '.ts', '.java', '.go', '.rs', '.html', '.htm', '.css', '.json', '.xml', '.yaml', '.yml', '.toml', '.sh', '.bash', '.zsh', '.bat', '.ps1', '.cmd', '.md', '.rst', '.ini', '.cfg',} def quick_mode_check(filepath: str) -> str: """Quick extension-based mode selection.""" ext = Path(filepath).suffix.lower() if ext in BINARY_EXTENSIONS: return 'binary' elif ext in TEXT_EXTENSIONS: return 'text' else: return 'binary' # Default to binary for unknownModern text files often use UTF-8 encoding, which uses bytes > 127 for non-ASCII characters. ASCII mode won't corrupt these characters (they're just bytes), but it might still modify line endings unexpectedly. For UTF-8 files, binary mode is often the safer choice to ensure exact byte preservation.
Many FTP clients implement automatic transfer mode selection to reduce the burden on users. Understanding how these features work helps you configure them correctly and troubleshoot issues.
Auto-Detection Strategies:
| Client | Auto Mode Feature | Default Behavior | Override Options |
|---|---|---|---|
| FileZilla | Auto | Extension-based lists | Fully configurable list editor |
| WinSCP | Automatic | Binary default + text list | Can force binary-only |
| Cyberduck | Automatic | System MIME types | Per-transfer override |
| lftp (CLI) | auto-ascii | Off by default | ascii-extensions config |
| curl | Manual only | Binary always | --use-ascii flag for ASCII |
| Python ftplib | Manual only | None | User must call storlines/storbinary |
12345678910111213141516171819202122232425262728293031323334353637383940414243
# FileZilla ASCII Mode Extension List (Settings → Transfers → File Types) # Default ASCII extensions in FileZilla:# File Types marked for ASCII mode:.txt.htm.html.php.asp.js.css.xml.xsl.bat.sh # Adding custom extensions:# Settings → Transfers → File Types → Add# Type: .py# Mode: ASCII # Or switch to "Binary" default with ASCII exceptions:# Settings → Transfers → File Types# Default transfer type: Binary# (Then only listed extensions use ASCII) # ============================================# WinSCP Configuration (via INI file) [Configuration\Interface]; 0 = Binary, 1 = Ascii, 2 = Automatic DefaultTransferType=2AsciiFileMask=*.txt;*.html;*.htm;*.sh;*.bat;*.css;*.js # ============================================# lftp Configuration (~/.lftprc) # Enable ASCII mode for specific extensionsset auto-ascii onset ascii-extensions "txt,html,htm,css,js,sh,bat,py" # Force binary mode globally (override auto)# set auto-ascii offAuto-detection can fail: (1) Files without extensions have no extension to match, (2) Misleading extensions (photo.txt that's actually binary), (3) New file types not in the client's database. For critical transfers, especially automated ones, explicitly set the mode rather than relying on auto-detection.
Automated FTP scripts and applications require careful mode selection strategy. Human intuition isn't available to make case-by-case decisions, so the system must be configured correctly from the start.
Strategy 1: Always Binary
The simplest and safest approach for most automated systems:
123456789101112131415161718192021222324252627282930
from ftplib import FTPfrom pathlib import Path def transfer_file(ftp: FTP, local_path: Path, remote_path: str): """ Transfer file using binary mode always. Rationale: - Binary mode never corrupts files - Text file line endings can be fixed post-transfer if needed - Simpler code with no mode detection logic - Eliminates mode-related bugs entirely """ ftp.voidcmd('TYPE I') # Always binary with open(local_path, 'rb') as f: ftp.storbinary(f'STOR {remote_path}', f) def batch_upload(ftp: FTP, local_dir: Path, remote_dir: str): """Upload all files in directory using binary mode.""" ftp.voidcmd('TYPE I') # Set once for all transfers for file in local_dir.iterdir(): if file.is_file(): remote_path = f"{remote_dir}/{file.name}" with open(file, 'rb') as f: ftp.storbinary(f'STOR {remote_path}', f) print(f"Uploaded (binary): {file.name}")Strategy 2: Extension-Based Selection
For systems that genuinely need ASCII mode for cross-platform text compatibility:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
from ftplib import FTPfrom pathlib import Pathfrom typing import Set # Define ASCII-safe extensions (conservative list)ASCII_EXTENSIONS: Set[str] = { '.txt', '.csv', '.log', '.sh', '.bash', '.bat', '.cmd', # Shell scripts - line endings matter! '.sql', # SQL scripts} # Everything else is binary (including source code, to preserve UTF-8) def get_transfer_mode(filename: str) -> str: """Determine transfer mode based on extension.""" ext = Path(filename).suffix.lower() return 'A' if ext in ASCII_EXTENSIONS else 'I' def smart_transfer( ftp: FTP, local_path: Path, remote_path: str, force_mode: str = None): """ Transfer file with smart mode selection. force_mode: 'A' for ASCII, 'I' for Binary, None for auto """ if force_mode: mode = force_mode else: mode = get_transfer_mode(local_path.name) ftp.voidcmd(f'TYPE {mode}') if mode == 'A': # ASCII mode - use text methods with open(local_path, 'r', encoding='utf-8') as f: ftp.storlines(f'STOR {remote_path}', f) print(f"Uploaded (ASCII): {local_path.name}") else: # Binary mode - use binary methods with open(local_path, 'rb') as f: ftp.storbinary(f'STOR {remote_path}', f) print(f"Uploaded (binary): {local_path.name}")Strategy 3: Metadata-Driven Selection
For sophisticated systems, store mode preferences alongside files:
12345678910111213141516171819202122232425262728293031
# transfer-manifest.yaml# Define transfer specifications for automated uploads/downloads defaults: mode: binary retry_count: 3 verify_checksum: true files: - path: config/settings.ini mode: ascii description: Application configuration (cross-platform) - path: scripts/*.sh mode: ascii description: Shell scripts requiring Unix line endings - path: data/*.csv mode: ascii description: Data exports for cross-platform processing - path: binaries/* mode: binary description: Compiled executables - path: uploads/**/* mode: binary description: User uploads (unknown content) # Patterns matched in order; first match wins# Unmatched files use 'defaults.mode'For automated systems: start with 'always binary' mode. Only add ASCII mode support if you have specific, documented requirements for cross-platform text file compatibility. ASCII mode adds complexity without benefit for most file types. If you need line ending conversion, do it locally before/after transfer using dedicated tools (dos2unix, etc.).
When files arrive corrupted or display incorrectly after FTP transfer, mode issues are a prime suspect. Here's a systematic approach to diagnosis and resolution.
Symptom 1: Binary File Doesn't Work After Transfer
1234567891011121314151617181920212223242526272829303132
# Symptoms:# - Image won't open: "File is corrupted or unsupported format"# - Executable crashes or won't run# - Archive fails to extract: "Unexpected end of archive"# - PDF shows "Failed to load PDF document" # Diagnosis steps: # 1. Check file sizes$ ls -l original.zip transferred.zip-rw-r--r-- 1 user user 1048576 Jan 15 10:00 original.zip-rw-r--r-- 1 user user 1048123 Jan 15 10:05 transferred.zip# ^^^^^^ DIFFERENT! Mode was wrong # 2. Compare checksums (will fail if corrupted)$ md5sum original.zipabc123def456... original.zip$ md5sum transferred.zip 789xyz000111... transferred.zip # DIFFERENT = CORRUPTED # 3. Look for 0D 0A pattern changes$ hexdump -C original.zip | grep "0d 0a"00000010 ... 4b 0d 0a 1a 00 ... # Has 0D 0A$ hexdump -C transferred.zip | grep "0d 0a"(no output) # 0D 0A became just 0A! # Solution: Re-transfer in binary mode$ ftp serverftp> binary200 Switching to Binary modeftp> get original.zip transferred.zip226 Transfer completeSymptom 2: Text File Has Wrong Line Endings
1234567891011121314151617181920212223242526272829303132333435363738394041
# Symptoms:# - Script fails with "\r: command not found"# - File displays on one line in simple editors# - ^M characters visible at end of lines# - Git shows entire file as changed (whitespace differences) # Diagnosis: # Check file's actual line endings$ file script.shscript.sh: ASCII text, with CRLF line terminators # Problem! $ cat -A script.sh#!/bin/bash^M$echo "Hello"^M$ # ^M is visible CR character # Or use od to see bytes$ od -c script.sh | head -20000000 # ! / b i n / b a s h \r e c h# ^^^ CR+LF # Solutions: # Option 1: Convert with dos2unix$ dos2unix script.shdos2unix: converting file script.sh to Unix format... # Option 2: Use sed$ sed -i 's/\r$//' script.sh # Option 3: Use tr$ tr -d '\r' < script.sh > script_fixed.sh # Option 4: In vim:set ff=unix:wq # Prevention: Transfer shell scripts in ASCII mode, or# convert before transfer, or # use binary + convert after| Symptom | Likely Cause | Solution |
|---|---|---|
| File size changed | ASCII mode on binary file | Re-transfer in binary mode |
| Binary file won't open | ASCII mode corrupted it | Re-transfer in binary mode |
| ^M at line ends | Binary mode for text file | Run dos2unix or unix2dos |
| Script: command not found | CRLF in shell script | Convert to Unix line endings |
| File shows as one line | LF-only file on old Windows app | Run unix2dos or use modern editor |
| Checksum mismatch | Wrong transfer mode | Re-transfer in binary mode |
| ZIP/archive corrupt | ASCII mode mangled bytes | Re-transfer in binary mode |
Develop the habit of verifying transfer mode before starting, especially for automated scripts. Log the mode used for each transfer. Keep original files until successful transfer is verified. These practices save hours of debugging.
We've covered the complete landscape of FTP transfer modes, from the practical TYPE command to deep understanding of corruption scenarios and troubleshooting. Let's consolidate the key takeaways:
What's Next:
We've covered authenticated and standard FTP usage. The final page explores Anonymous FTP—the mode that allows public access to files without requiring user credentials, and its role in software distribution and public data sharing.
You now understand FTP transfer modes in depth: how ASCII and binary modes work, when to use each, how files get corrupted, and how to troubleshoot mode-related issues. Next, we'll explore anonymous FTP access for public file sharing.