[libgdata] documents: Switch to a stream-based download API



commit 3f3fd8ca71d10bb24b5b3ee8b12325f46f17cf34
Author: Philip Withnall <philip tecnocode co uk>
Date:   Wed Dec 8 15:06:38 2010 +0000

    documents: Switch to a stream-based download API
    
    Downloading to a file provided by its path was unnecessarily limiting,
    forcing downloads to be made directly to disk instead of (e.g.) another
    network stream, and subjecting clients to libgdata's idea of â??Replace this
    File?â?? logic. It's not much extra code for the clients to handle this
    themselves, and offers much greater flexibility. We also get async support
    for free, as creating the GDataDownloadStream is a cheap operation, and it
    already has async support.
    
    This updates the test suite appropriately.
    
    This is an API break which affects:
     â?¢ gdata_documents_document_download()

 .../services/documents/gdata-documents-document.c  |   69 +++-----------
 .../services/documents/gdata-documents-document.h  |    6 +-
 gdata/tests/documents.c                            |  101 ++++++++++---------
 3 files changed, 68 insertions(+), 108 deletions(-)
---
diff --git a/gdata/services/documents/gdata-documents-document.c b/gdata/services/documents/gdata-documents-document.c
index 1b83a1f..4fd9b35 100644
--- a/gdata/services/documents/gdata-documents-document.c
+++ b/gdata/services/documents/gdata-documents-document.c
@@ -57,21 +57,11 @@ gdata_documents_document_init (GDataDocumentsDocument *self)
 	/* Nothing to see here. */
 }
 
-static void
-notify_content_type_cb (GDataDownloadStream *download_stream, GParamSpec *pspec, gchar **content_type)
-{
-	*content_type = g_strdup (gdata_download_stream_get_content_type (download_stream));
-}
-
 /**
  * gdata_documents_document_download:
  * @self: a #GDataDocumentsDocument
  * @service: a #GDataDocumentsService
- * @content_type: (out callee-allocates) (transfer full) (allow-none): return location for the document's content type, or %NULL; free with g_free()
  * @export_format: the format in which the document should be exported
- * @destination_file: the #GFile into which the text file should be saved
- * @replace_file_if_exists: %TRUE if the file should be replaced if it already exists, %FALSE otherwise
- * @cancellable: optional #GCancellable object, or %NULL
  * @error: a #GError, or %NULL
  *
  * Downloads and returns the document file represented by the #GDataDocumentsDocument. If the document doesn't exist, %NULL is returned, but no error
@@ -85,38 +75,28 @@ notify_content_type_cb (GDataDownloadStream *download_stream, GParamSpec *pspec,
  * <ulink type="http" url="http://code.google.com/apis/documents/docs/2.0/developers_guide_protocol.html#DownloadingSpreadsheets";>GData protocol
  * specification</ulink> for more information.
  *
- * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread. If the operation was
- * cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
- *
- * If @destination_file is a directory, then the file will be downloaded into this directory with a filename based on the #GDataEntry's title and the
- * export format. If @replace_file_if_exists is set to %FALSE and the destination file already exists, a %G_IO_ERROR_EXISTS will be returned.
+ * To get the content type of the downloaded file, gdata_download_stream_get_content_type() can be called on the returned #GDataDownloadStream.
+ * Calling gdata_download_stream_get_content_length() on the stream will not return a meaningful result, however, as the stream is encoded in chunks,
+ * rather than by content length.
  *
  * If @service isn't authenticated, a %GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED will be returned.
  *
  * If there is an error getting the document, a %GDATA_SERVICE_ERROR_PROTOCOL_ERROR error will be returned.
  *
- * Return value: (transfer full): a #GFile pointing to the downloaded document, or %NULL; unref with g_object_unref()
+ * Return value: (transfer full): a #GDataDownloadStream to download the document with, or %NULL; unref with g_object_unref()
  *
- * Since: 0.7.0
+ * Since: 0.8.0
  **/
-GFile *
-gdata_documents_document_download (GDataDocumentsDocument *self, GDataDocumentsService *service, gchar **content_type, const gchar *export_format,
-                                   GFile *destination_file, gboolean replace_file_if_exists, GCancellable *cancellable, GError **error)
+GDataDownloadStream *
+gdata_documents_document_download (GDataDocumentsDocument *self, GDataDocumentsService *service, const gchar *export_format, GError **error)
 {
-	const gchar *document_title;
-	gchar *default_filename, *download_uri;
+	gchar *download_uri;
 	GDataService *_service;
-	GFileOutputStream *dest_stream;
-	GInputStream *src_stream;
-	GFile *output_file = NULL;
-	GError *child_error = NULL;
+	GDataDownloadStream *download_stream;
 
-	/* TODO: async version */
 	g_return_val_if_fail (GDATA_IS_DOCUMENTS_DOCUMENT (self), NULL);
 	g_return_val_if_fail (GDATA_IS_SERVICE (service), NULL);
 	g_return_val_if_fail (export_format != NULL && *export_format != '\0', NULL);
-	g_return_val_if_fail (G_IS_FILE (destination_file), NULL);
-	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
 	/* Horrible hack to force use of the spreadsheet service if the document we're downloading is a spreadsheet. This is necessary because it's
@@ -133,37 +113,12 @@ gdata_documents_document_download (GDataDocumentsDocument *self, GDataDocumentsS
 		return NULL;
 	}
 
-	/* Determine a default filename based on the document's title and export format */
-	document_title = gdata_entry_get_title (GDATA_ENTRY (self));
-	default_filename = g_strdup_printf ("%s.%s", document_title, export_format);
-
-	dest_stream = _gdata_download_stream_find_destination (default_filename, destination_file, &output_file, replace_file_if_exists, cancellable,
-	                                                       error);
-	g_free (default_filename);
-
-	if (dest_stream == NULL)
-		return NULL;
-
-	/* Get the download URI */
+	/* Get the download URI and create a stream for it */
 	download_uri = gdata_documents_document_get_download_uri (self, export_format);
-	g_assert (download_uri != NULL);
-
-	/* Synchronously splice the data from the download stream to the file stream (network -> disk) */
-	src_stream = gdata_download_stream_new (_service, download_uri);
-	g_signal_connect (src_stream, "notify::content-type", (GCallback) notify_content_type_cb, content_type);
-	g_output_stream_splice (G_OUTPUT_STREAM (dest_stream), src_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
-	                        cancellable, &child_error);
-	g_object_unref (src_stream);
-	g_object_unref (dest_stream);
+	download_stream = GDATA_DOWNLOAD_STREAM (gdata_download_stream_new (_service, download_uri));
 	g_free (download_uri);
 
-	if (child_error != NULL) {
-		g_object_unref (output_file);
-		g_propagate_error (error, child_error);
-		return NULL;
-	}
-
-	return output_file;
+	return download_stream;
 }
 
 /**
diff --git a/gdata/services/documents/gdata-documents-document.h b/gdata/services/documents/gdata-documents-document.h
index 58c7a05..b52a980 100644
--- a/gdata/services/documents/gdata-documents-document.h
+++ b/gdata/services/documents/gdata-documents-document.h
@@ -23,6 +23,7 @@
 #include <glib.h>
 #include <glib-object.h>
 
+#include <gdata/gdata-download-stream.h>
 #include <gdata/services/documents/gdata-documents-entry.h>
 #include <gdata/services/documents/gdata-documents-service.h>
 
@@ -63,9 +64,8 @@ typedef struct {
 
 GType gdata_documents_document_get_type (void) G_GNUC_CONST;
 
-GFile *gdata_documents_document_download (GDataDocumentsDocument *self, GDataDocumentsService *service, gchar **content_type,
-                                          const gchar *export_format, GFile *destination_file, gboolean replace_file_if_exists,
-                                          GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+GDataDownloadStream *gdata_documents_document_download (GDataDocumentsDocument *self, GDataDocumentsService *service, const gchar *export_format,
+                                                        GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
 gchar *gdata_documents_document_get_download_uri (GDataDocumentsDocument *self, const gchar *export_format) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
 
 G_END_DECLS
diff --git a/gdata/tests/documents.c b/gdata/tests/documents.c
index 6d6b2d9..892fea8 100644
--- a/gdata/tests/documents.c
+++ b/gdata/tests/documents.c
@@ -454,70 +454,75 @@ test_download_all_documents (gconstpointer service)
 {
 	GDataDocumentsFeed *feed;
 	GError *error = NULL;
-	gchar *content_type = NULL;
-	GFile *destination_file;
-	gchar *destination_file_name;
-	GString *destination_display_name;
 	GList *i;
-	gint ods_nb = 0, odt_nb = 0;
 
 	feed = gdata_documents_service_query_documents (GDATA_DOCUMENTS_SERVICE (service), NULL, NULL, NULL, NULL, &error);
 	g_assert_no_error (error);
 	g_assert (GDATA_IS_FEED (feed));
 
 	for (i = gdata_feed_get_entries (GDATA_FEED (feed)); i != NULL; i = i->next) {
+		GDataDownloadStream *download_stream;
+		GFileOutputStream *output_stream;
+		GFile *destination_file;
+		GFileInfo *file_info;
+		const gchar *destination_file_extension;
+		gchar *destination_file_name, *destination_file_path;
+
 		if (GDATA_IS_DOCUMENTS_PRESENTATION (i->data)) {
-			destination_file = g_file_new_for_path ("/tmp");
-			destination_file = gdata_documents_document_download (GDATA_DOCUMENTS_DOCUMENT (i->data), GDATA_DOCUMENTS_SERVICE (service),
-			                                                      &content_type, GDATA_DOCUMENTS_PRESENTATION_PPT, destination_file,
-			                                                      TRUE, NULL, &error);
+			/* Presentation */
+			destination_file_extension = "odp";
+			download_stream = gdata_documents_document_download (GDATA_DOCUMENTS_DOCUMENT (i->data), GDATA_DOCUMENTS_SERVICE (service),
+			                                                     GDATA_DOCUMENTS_PRESENTATION_PPT, &error);
 		} else if (GDATA_IS_DOCUMENTS_SPREADSHEET (i->data)) {
-			destination_file_name = g_strdup_printf ("/tmp/%s.%s", gdata_documents_entry_get_document_id (GDATA_DOCUMENTS_ENTRY (i->data)), "ods");
-			destination_file = g_file_new_for_path (destination_file_name);
-			g_free (destination_file_name);
-			destination_file = gdata_documents_document_download (GDATA_DOCUMENTS_DOCUMENT (i->data), GDATA_DOCUMENTS_SERVICE (service),
-			                                                      &content_type, GDATA_DOCUMENTS_SPREADSHEET_ODS, destination_file,
-			                                                      TRUE, NULL, &error);
-			g_assert_no_error (error);
-
-			destination_display_name = g_string_new (gdata_entry_get_title (GDATA_ENTRY(i->data)));
-			g_string_append (destination_display_name, ".ods");
-			g_file_set_display_name (destination_file, destination_display_name->str, NULL, &error);
-			while (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
-				g_clear_error (&error);
-				error = NULL;
-				g_string_printf (destination_display_name,"%s%d.%s", gdata_entry_get_title (GDATA_ENTRY(i->data)), ods_nb, "ods");
-				g_file_set_display_name (destination_file, destination_display_name->str, NULL, &error);
-				ods_nb++;
-			}
-			g_string_free (destination_display_name, TRUE);
+			/* Spreadsheet */
+			destination_file_extension = "ods";
+			download_stream = gdata_documents_document_download (GDATA_DOCUMENTS_DOCUMENT (i->data), GDATA_DOCUMENTS_SERVICE (service),
+			                                                     GDATA_DOCUMENTS_SPREADSHEET_ODS, &error);
 		} else if (GDATA_IS_DOCUMENTS_TEXT (i->data)) {
-			destination_file_name = g_strdup_printf ("/tmp/%s.%s", gdata_documents_entry_get_document_id (GDATA_DOCUMENTS_ENTRY (i->data)), "odt");
-			destination_file = g_file_new_for_path (destination_file_name);
-			g_free (destination_file_name);
-			destination_file = gdata_documents_document_download (GDATA_DOCUMENTS_DOCUMENT (i->data), GDATA_DOCUMENTS_SERVICE (service),
-			                                                      &content_type, GDATA_DOCUMENTS_TEXT_ODT, destination_file, TRUE, NULL,
-			                                                      &error);
-			g_assert_no_error (error);
-
-			destination_display_name = g_string_new (gdata_entry_get_title (GDATA_ENTRY(i->data)));
-			g_string_append (destination_display_name, ".odt");
-			g_file_set_display_name (destination_file, destination_display_name->str, NULL, &error);
-			while (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
-				g_clear_error (&error);
-				g_string_printf (destination_display_name,"%s%d.%s", gdata_entry_get_title (GDATA_ENTRY(i->data)), odt_nb, "odt");
-				g_file_set_display_name (destination_file, destination_display_name->str, NULL, &error);
-				odt_nb++;
-			}
-			g_string_free (destination_display_name, TRUE);
+			/* Text document */
+			destination_file_extension = "odt";
+			download_stream = gdata_documents_document_download (GDATA_DOCUMENTS_DOCUMENT (i->data), GDATA_DOCUMENTS_SERVICE (service),
+			                                                     GDATA_DOCUMENTS_TEXT_ODT, &error);
+		} else {
+			/* Error! */
+			g_assert_not_reached ();
 		}
 
 		g_assert_no_error (error);
-		g_free (content_type);
+
+		/* Find a destination file */
+		destination_file_name = g_strdup_printf ("%s.%s", gdata_documents_entry_get_document_id (GDATA_DOCUMENTS_ENTRY (i->data)),
+		                                         destination_file_extension);
+		destination_file_path = g_build_filename (g_get_tmp_dir (), destination_file_name, NULL);
+		g_free (destination_file_name);
+
+		destination_file = g_file_new_for_path (destination_file_path);
+		g_free (destination_file_path);
+
+		/* Download the file */
+		output_stream = g_file_replace (destination_file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, &error);
+		g_assert_no_error (error);
+
+		g_output_stream_splice (G_OUTPUT_STREAM (output_stream), G_INPUT_STREAM (download_stream),
+		                        G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, &error);
+		g_object_unref (output_stream);
+		g_assert_no_error (error);
+
+		/* Check the filesize */
+		file_info = g_file_query_info (destination_file, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, &error);
+		g_assert_no_error (error);
+
+		g_assert (g_file_info_get_size (file_info) > 0);
+		/* Checking the content types turns out to be quite involved, and not worth doing as it depends on the local user's content type DB */
+
+		g_object_unref (download_stream);
+
+		/* Delete the file (shouldn't cause the test to fail if this fails) */
+		g_file_delete (destination_file, NULL, NULL);
+		g_object_unref (destination_file);
 	}
 
 	g_object_unref (feed);
-	g_clear_error (&error);
 }
 
 static void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]