I couldn't see this noted anywhere...
ftp_nb_put apparently takes a much much longer time to upload the file than ftp_put (I haven't done any packet sniffing or logging tests to find out why). I was using a script, nearly identical to the example above, and a 100KB file had only uploaded 3.99KB after about 8 minutes! The php script naturally timed out before it was complete.
I changed my function to use ftp_put, got rid of the loop to check FTP_MOREDATA (as you will see in the example above), and the same script uploaded 2.2MB within 30 seconds with no other changes.
If you're using this function instead of ftp_put *purely to try to speed up your script* and it's taking a long time, you might want to try ftp_put instead.