This is really odd, but if anybody runs into the same issue, it looks like Android deviates a bit from the http spec and requires attachment filenames to be enclosed in double quotes. So to send a file called 'book.mobi', I needed the following header:
String contentHeader = "Content-Disposition: attachment; filename=\"book.mobi\"\r\n"
And adding a short delay(5) solved the crashing issue, which I guess must have been an overflowing buffer, and I also added a loop to ensure that the client stays connected. So I wound up with this, which seems to work pretty well on both my laptop and phone:
void sendRawFile(String fn, File file) {
Serial.print("Available memory: ");
Serial.println(system_get_free_heap_size());
int fSize = file.fileSize();
String response = "HTTP/1.1 200 OK\r\n";
// Test with .mobi ebooks, but application/octet-stream would probably work fine.
response += "Content-Type: application/x-mobipocket-ebook\r\n";
response += "Content-Disposition: attachment; filename=\"" + fn + "\"\r\n";
response += "Connection: keep-alive\r\n";
response += "Access-Control-Allow-Origin: *\r\n";
response += "Content-Length: ";
response += fSize;
response += "\r\n";
response += "\r\n";
server.client().setNoDelay(true);
server.client().write(response.c_str(), response.length());
Serial.println(response);
// Stream the file since these chips have very little memory.
int maxWaitTicks = 50;
int ticksWaited = 0;
int readResult = 0;
int fChunk = 512;
int progress = 0;
char responseBlob[fChunk];
// In case the file doesn't get re-opened before being dl'd again,
// reset the position after it's read.
FatPos_t fileStart;
file.getpos(&fileStart);
while (progress < fSize) {
readResult = file.read(responseBlob, fChunk);
server.client().write(responseBlob, fChunk);
progress += fChunk;
// For debugging - will slow DL speed.
/*if (progress % 16384 == 0) {
Serial.println(progress);
if (server.client().connected()) {
Serial.println("client connected.");
}
}*/
delay(5);
while (!server.client().connected()) {
delay(5);
ticksWaited++;
if (ticksWaited >= maxWaitTicks) {
// We're probably not going to reconnect after this long.
// Reset the file position and return.
Serial.println("Timeout...");
file.setpos(&fileStart);
return;
}
}
ticksWaited = 0;
}
file.setpos(&fileStart);
}
At 25-30 KB/s (15-20 if you print to the serial monitor) it's pretty slow, and I get the occasional timeout, but it's a good start. Bumping up the size of the file chunks might help speed it up. Thanks for the help!