Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
QTFileTransfer.c
////////// |
// |
// File: QTFileTransfer.c |
// |
// Contains: Sample code for transferring a file asynchronously from a web server. |
// |
// Written by: Tim Monroe |
// |
// Copyright: © 1998-1999 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <8> 07/20/00 rtm changed return type of completion routines to PASCAL_RTN void |
// <7> 03/21/00 rtm made changes to run under CarbonLib |
// <6> 10/01/99 rtm made gDataBuffer global, so we can dispose of it in the |
// QTFileTrans_CloseDownHandlers function; added calls to |
// DisposeRoutineDescriptor |
// <5> 03/03/99 rtm switched to using DataHReadAsync; removed unnecessary NOTES |
// <4> 12/25/99 rtm added NOTES concerning DataHReadAsync and DataHGetFileSize |
// <3> 11/30/98 rtm modified code to use GetDataHandler instead of FindNextComponent; |
// moved call to QTFileTrans_CloseDownHandlers out of completion proc |
// <2> 11/16/98 rtm got asynchronous ftp and http file transfer working |
// <1> 11/11/98 rtm first file |
// |
// QuickTime Streaming has ftp and http data handlers, which you can use to transfer files |
// synchronously or asynchronously from a web server. This sample code illustrates how to |
// perform asynchronous transfers. In all likelihood, you'll want to transfer asynchronously, |
// since your application can continue to operate while the transfer is underway. |
// |
// The basic idea is to instantiate the URL data handler and the HFS data handler; the URL |
// data handler will be reading data from a remote ftp or http file into a buffer, and the |
// HFS data handler will be writing data from that buffer into a local file. This reading and |
// writing continues until the file is completely transferred. |
// |
// To transfer a remote file to the local machine, call the QTFileTrans_CopyRemoteFileToLocalFile |
// function defined here. It does all the necessary set-up and schedules the first read request; |
// all subsequent write and read requests are scheduled by the read and write completion routines. |
// Note that, when doing asynchronous transfers, you need to give time to the data handlers by |
// calling DataHTask periodically; on the Mac, you can put code like this in your main event loop: |
// |
// // if we're done, close down the data handlers |
// if (gDoneTransferring) |
// QTFileTrans_CloseDownHandlers(); |
// |
// // give the data handlers some time, if they are still active |
// if (gDataReader != NULL) |
// DataHTask(gDataReader); |
// |
// if (gDataWriter != NULL) |
// DataHTask(gDataWriter); |
// |
// On Windows, you could install a timer that calls this code at a specified interval. (On either |
// platform, you should probably also implement some way of making sure that the user doesn't quit |
// the application while a transfer is underway.) |
// |
// NOTES: |
// |
// *** (1) *** |
// For information about the main routines used here, see the chapter "Data Handler Components" in |
// the document QT3.0Reference.pdf. |
// |
// *** (2) *** |
// The code for implementing synchronous transfers is actually much simpler: you don't need any |
// completion routines, and the "scheduling" is much easier. Here's an outline of all you need to do |
// to transfer a file synchronously: |
// |
// DataHGetData(gDataReader, gDataBuffer, 0L, 0L, kDataBufferSize); |
// DataHCloseForRead(gDataReader); |
// DataHPutData(gDataWriter, gDataBuffer, 0L, NULL, kDataBufferSize); |
// DataHCloseForWrite(gDataWriter); |
// |
// In this case, however, gDataBuffer is a handle, not a pointer. (Also, we've assumed that the file |
// being transferred fits completely into the buffer; you could easily fix that assumption.) |
// |
// *** (3) *** |
// You'll notice that we use our completion routines to schedule subsequent data reads and writes. |
// This is okay because data handler completion routines are never called at interrupt time. |
// |
// *** (4) *** |
// In some instances, DataHGetFileSize is not able to determine the size of the file to be downloaded |
// (for example, an FTP server might not support the SIZE command). A more general strategy therefore |
// would be to download a file until you get eofErr. Implementing this strategy is left as an exercise |
// for the reader. |
// |
////////// |
#include "QTFileTransfer.h" |
//global variables |
Ptr gDataBuffer = NULL; // buffer that holds data being transferred |
ComponentInstance gDataReader = NULL; // the data handler that reads data from the URL |
ComponentInstance gDataWriter = NULL; // the data handler that writes data to an HFS file |
DataHCompletionUPP gReadDataHCompletionUPP = NULL; |
DataHCompletionUPP gWriteDataHCompletionUPP = NULL; |
long gBytesToTransfer = 0L; // the number of bytes to transfer |
long gBytesTransferred = 0L; // the number of bytes already transferred |
Boolean gDoneTransferring = false; // are we done transferring data? |
////////// |
// |
// QTFileTrans_CopyRemoteFileToLocalFile |
// Copy a remote file (located at the specified URL) into a local file. |
// |
////////// |
OSErr QTFileTrans_CopyRemoteFileToLocalFile (char *theURL, FSSpecPtr theFSSpecPtr) |
{ |
Handle myReaderRef = NULL; // data reference for the remote file |
Handle myWriterRef = NULL; // data reference for the local file |
Size mySize = 0; |
ComponentResult myErr = badComponentType; |
////////// |
// |
// create a data reference for the remote file |
// |
////////// |
// get the size of the URL, plus the terminating null byte |
mySize = (Size)strlen(theURL) + 1; |
if (mySize == 0) |
goto bail; |
// allocate a new handle |
myReaderRef = NewHandleClear(mySize); |
if (myReaderRef == NULL) |
goto bail; |
// copy the URL into the handle |
BlockMove(theURL, *myReaderRef, mySize); |
////////// |
// |
// create a data reference for the local file |
// |
////////// |
// delete the target local file, if it already exists; |
// if it doesn't exist yet, we'll get an error (fnfErr), which we just ignore |
FSpDelete(theFSSpecPtr); |
myWriterRef = NewHandleClear(sizeof(Handle)); |
if (myWriterRef == NULL) |
goto bail; |
// create the local file |
myErr = FSpCreate(theFSSpecPtr, kTransFileCreator, kTransFileType, smSystemScript); |
if (myErr != noErr) |
goto bail; |
myErr = QTNewAlias(theFSSpecPtr, (AliasHandle *)&myWriterRef, true); |
if (myErr != noErr) |
goto bail; |
////////// |
// |
// find and open the Apple URL and HFS data handlers; connect the data references to them |
// |
////////// |
gDataReader = OpenComponent(GetDataHandler(myReaderRef, URLDataHandlerSubType, kDataHCanRead)); |
if (gDataReader == NULL) |
goto bail; |
gDataWriter = OpenComponent(GetDataHandler(myWriterRef, rAliasType, kDataHCanWrite)); |
if (gDataWriter == NULL) |
goto bail; |
// set the data reference for the URL data handler |
myErr = DataHSetDataRef(gDataReader, myReaderRef); |
if (myErr != noErr) |
goto bail; |
// set the data reference for the HFS data handler |
myErr = DataHSetDataRef(gDataWriter, myWriterRef); |
if (myErr != noErr) |
goto bail; |
////////// |
// |
// allocate a data buffer; the URL data handler copies data into this buffer, |
// and the HFS data handler copies data out of it |
// |
////////// |
gDataBuffer = NewPtrClear(kDataBufferSize); |
myErr = MemError(); |
if (myErr != noErr) |
goto bail; |
////////// |
// |
// connect to the remote and local files |
// |
////////// |
// open a read-only path to the remote data reference |
myErr = DataHOpenForRead(gDataReader); |
if (myErr != noErr) |
goto bail; |
// get the size of the remote file |
myErr = DataHGetFileSize(gDataReader, &gBytesToTransfer); |
if (myErr != noErr) |
goto bail; |
// open a write-only path to the local data reference |
myErr = DataHOpenForWrite(gDataWriter); |
if (myErr != noErr) |
goto bail; |
////////// |
// |
// start reading and writing data |
// |
////////// |
gDoneTransferring = false; |
gBytesTransferred = 0L; |
gReadDataHCompletionUPP = NewDataHCompletionUPP(QTFileTrans_ReadDataCompletionProc); |
gWriteDataHCompletionUPP = NewDataHCompletionUPP(QTFileTrans_WriteDataCompletionProc); |
// start retrieving the data; we do this by calling our own write completion routine, |
// pretending that we've just successfully finished writing 0 bytes of data |
QTFileTrans_WriteDataCompletionProc(gDataBuffer, 0L, noErr); |
bail: |
// if we encountered any error, close the data handler components |
if (myErr != noErr) |
QTFileTrans_CloseDownHandlers(); |
return((OSErr)myErr); |
} |
////////// |
// |
// QTFileTrans_ReadDataCompletionProc |
// This procedure is called when the data handler has completed a read operation. |
// |
// The theRefCon parameter contains the number of bytes just read. |
// |
////////// |
PASCAL_RTN void QTFileTrans_ReadDataCompletionProc (Ptr theRequest, long theRefCon, OSErr theErr) |
{ |
#pragma unused(theErr) |
// we just finished reading some data, so schedule a write operation |
DataHWrite( gDataWriter, |
theRequest, // the data buffer |
gBytesTransferred, // write from the current offset |
theRefCon, // the number of bytes to write |
gWriteDataHCompletionUPP, |
theRefCon); |
} |
////////// |
// |
// QTFileTrans_WriteDataCompletionProc |
// This procedure is called when the data handler has completed a write operation. |
// |
// The theRefCon parameter contains the number of bytes just written. |
// |
////////// |
PASCAL_RTN void QTFileTrans_WriteDataCompletionProc (Ptr theRequest, long theRefCon, OSErr theErr) |
{ |
#pragma unused(theErr) |
long myNumBytesToRead; |
wide myWide; |
// increment our tally of the number of bytes written so far |
gBytesTransferred += theRefCon; |
if (gBytesTransferred < gBytesToTransfer) { |
// there is still data to read and write, so schedule a read operation |
// determine how big a chunk to read |
if (gBytesToTransfer - gBytesTransferred > kDataBufferSize) |
myNumBytesToRead = kDataBufferSize; |
else |
myNumBytesToRead = gBytesToTransfer - gBytesTransferred; |
myWide.lo = gBytesTransferred; // read from the current offset |
myWide.hi = 0; |
// schedule a read operation |
DataHReadAsync(gDataReader, |
theRequest, // the data buffer |
myNumBytesToRead, |
&myWide, |
gReadDataHCompletionUPP, |
myNumBytesToRead); |
} else { |
// we've transferred all the data, so set a flag to tell us to close down the data handlers |
gDoneTransferring = true; |
} |
} |
////////// |
// |
// QTFileTrans_CloseDownHandlers |
// Close our read/write access to our data references and then close down the read/write data handlers. |
// |
////////// |
void QTFileTrans_CloseDownHandlers (void) |
{ |
if (gDataReader != NULL) { |
DataHCloseForRead(gDataReader); |
CloseComponent(gDataReader); |
gDataReader = NULL; |
} |
if (gDataWriter != NULL) { |
DataHCloseForWrite(gDataWriter); |
CloseComponent(gDataWriter); |
gDataWriter = NULL; |
} |
// dispose of the data buffer |
if (gDataBuffer != NULL) |
DisposePtr(gDataBuffer); |
// dispose of the routine descriptors |
if (gReadDataHCompletionUPP != NULL) |
DisposeDataHCompletionUPP(gReadDataHCompletionUPP); |
if (gWriteDataHCompletionUPP != NULL) |
DisposeDataHCompletionUPP(gWriteDataHCompletionUPP); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14