This recipe shows you the simplest ways to download a file through code, first in a command-line application and then from a web application. As an example, we download the front page of the Learning Dart website from http://learningdart.org.
A client program (be it web or command-line) receives content, such as files or web pages, from a web server using the HTTP protocol. The dart:html
and dart:io
package provides us with the basic classes we need to do this, which are as follows:
Uriclass
class (from dart:core
) has all we need to parse, encode, and decode web addresses; the method Uri.parse
is often usedHttpRequest
class (from dart:html
) has the getString
method to fetch a file from a URLHttpClientclass
class (from dart:io
) has all kinds of methods, such as get
and post
, to send a request (class HttpClientRequest
) to a web server and get a response (class HttpClientResponse
) backdownload_string.dart
, which is started from download_string.html
(these files can be found in the download_file
project) as shown in the following code:import 'dart:html'; main() { HttpRequest.getString('http://learningdart.org') .then(processString) .catchError(print); } processString(str) { print(str); }
download_file.dart
, we see the basic mechanism of how to do this for a command-line app as follows:import 'dart:io'; import 'dart:convert'; var client; main() { var url = Uri.parse('http://learningdart.org'), client = new HttpClient(); client.getUrl(url) .then((HttpClientRequest req) => req.close()) .then((HttpClientResponse resp) => writeToFile(resp)); } writeToFile(resp) { resp.transform(UTF8.decoder) .toList().then((data) { var body = data.join(''), var file = new File('dart.txt'), file.writeAsString(body).then((_) { client.close(); }); }); }
For a web app, we use the getString
method on HttpRequest
to fetch the file from the URL as one big string, which is asynchronously passed to processString
. It could do just about anything with the string it gets back, for example, if it were a JSON or XML string, we could parse this and get data out of it to show on our web page. So HttpRequestcan
is used to fetch data over HTTP and FTP protocols from a URL, without producing complete web page updates. This is, in fact, the way to make AJAX calls (or XMLHttpRequest
) and as a consequence, partial page updates. We will use it in the following recipe to fetch a large blob file.
Don't confuse this class with another class with the same name HttpRequest
from dart:io
, which must be used in server-side applications (we will use it extensively in the recipes of the following chapter). A web server, or more formally, an HTTP server, that listens for HTTP requests coming in on a specific host and port, generates such an object for each request it receives.
For a command-line app, first we transform the web address string to a Uri
object with the static method parse. Then we make an HttpClient
object and invoke the getUrl
request on URL (the Uri
object). This works in two steps, each returning a Future, which are:
.then
completes with a request object that has been made but not sent yet. In the callback, you can still change or add to the request headers or the body. A call to close
sends the request to the server. This step serves to make the request and send it..then
completes when the response object is received from the server, and you can access headers and the body (the body is available as a stream). This step serves to process the response; here, we call writeToFile
.In writeToFile
, we read the response data, transforming it from UTF-8 to a string (helped by the join
method that transforms the list into a string), and write it to a file with writeAsString
. When this finishes, the HttpClient
object is closed; this releases the network connections that have been made.
The following are some variations to accomplish the same thing for a command-line app:
The .then
variable in the command line can be simplified to the following:
.then((HttpClientResponse resp) => resp.pipe(new File('dart.txt').openWrite()));
The pipe
method on the response object can send the stream immediately to the file to be written. This will perform better when downloading bigger files.
An even more simplified approach can be taken using the http
package by the Dart team, which was made to facilitate coding requests and responses (see download_file2.dart
). Have a look at the following code:
import 'dart:io'; import 'package:http/http.dart' as http; main() { var url = Uri.parse('http://learningdart.org'), http.get(url).then((response) { new File('dart.txt').writeAsBytes(response.bodyBytes); }); }