Question

I am using parse for my chatting application. When I upload files, I keep the url, and send the url to other users, who can then download files via this URL.

Here is my code for uploading files:

+ (void)uploadBlob:(NSData *)blob fileName:(NSString *)fileName type:(NSString *)type {
    if ([self private_checkCloudForbidden]) {
        return;
    }

    if (CHOSEN_SERVER_DATABASE == ServersQuickBlox) {
        if ([Format isThumbnailWithBlobFileName:fileName]) {
            type = @"image";
        }

        NSString *qbContentType = @"";
        if ([type isEqualToString:@"image"]) {
            qbContentType = @"image/jpg";
        }

        [QBContent TUploadFile:blob fileName:fileName contentType:qbContentType isPublic:YES delegate:[CloudDelegate sharedInstance]];


    }

    else if (CHOSEN_SERVER_DATABASE == ServersParse) {

        PFFile *file = [PFFile fileWithName:fileName data:blob];

        [file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
            if (error){
                NSLog(@"Error: %@", [[error userInfo] objectForKey:@"error"]);
            } else {

                [CloudHandler didUploadBlobWithFileName:file.name blobID:file.url];

            }
        }];

    }
}

and in CloudHandler didUploadBlobWithFileName method, I will save the file's url as the blobID. And here is how I download files via the blobID/url when using QuickBlox:

+ (void)downloadWithBlobId: (NSString *)blobID {
    if ([self private_checkCloudForbidden]) {
        return;
    }


    if (CHOSEN_SERVER_DATABASE == ServersQuickBlox) {


        [QBContent TDownloadFileWithBlobID:blobID.integerValue delegate:[CloudDelegate sharedInstance]];

    }

    else if (CHOSEN_SERVER_DATABASE == ServersParse) {

        //TODO: HOW TO DOWNLOAD FILES?

    }

}

I didn't find the API to download file via URL. (it's a bit weird if parse does provide url or blobID that is useless

EDIT:

The reason I don't use attributes of type 'file':

1. I can't store 'file' on local database. It has to be the blobID or URL. 

2. when I send a rich message, I can send along the blobID, so that the receiver does not have to fetch the object first before downloading the binary. 
Was it helpful?

Solution

You should transfer the PFObject instead of the url. Like this:

PFObject *object; //the object you have
PFFile *soundFile = object[@"file"];
[soundFile getDataInBackgroundWithBlock:^(NSData *soundData, NSError *error) {
        if (!error) {
            [self saveSoundFileToDocuments:soundData fileName:object[@"fileName"]];
        }
    }
    progressBlock:^(int percentDone) {

    }];

- (void)saveSoundFileToDocuments:(NSData *)soundData fileName:(NSString *)fileName {
    NSString *docsDir;
    NSArray *dirPaths;

    dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    docsDir = [dirPaths objectAtIndex:0];
    NSString *databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:[DataAcesss encryptText:fileName]]];
    [soundData writeToFile:databasePath atomically:YES];
}

This way you can download the file and have the file name as well.

OTHER TIPS

The API supplied with UIKit for retrieving data from a URL is asynchronous, so as to leave the UI responsive. It uses the interface NSURLConnectionDelegate. You should implement that interface in order to receive the data asynchronously. First you initialize the retrieval from the URL like this:

NSURLRequest *theRequest=[NSURLRequest requestWithURL:anUrl
                                          cachePolicy:NSURLRequestUseProtocolCachePolicy
                                      timeoutInterval:CONNECTION_TIMEOUT];
// create the connection with the request
// and start loading the data
urlConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (urlConnection) {
    // Create the NSMutableData to hold the received data.
    // receivedData is an instance variable declared elsewhere.
    self.receivedData = [NSMutableData data];
} else {
    // Inform the user that the connection failed.
    NSLog(@"Failed to create connection to URL: %@.", anUrl);
}

Here the class containing this code is set as the delegate, so this class should be declared as implementing the named interface:

@interface MyClass : NSObject <NSURLConnectionDelegate> {
}
// URLConnection Delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse;

A current implementation of mine looks like this:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    // This method is called when the server has determined that it
    // has enough information to create the NSURLResponse.

    // It can be called multiple times, for example in the case of a
    // redirect, so each time we reset the data.

    // receivedData is an instance variable declared elsewhere.
    [receivedData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    // Append the new data to receivedData.
    // receivedData is an instance variable declared elsewhere.
    [receivedData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    // Do error handling here
    NSLog(@"Connection failed! Error - %@ %@",
          [error localizedDescription],
          [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);
    // Now process the received data accumulated in receivedData.
}

-(NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
    return cachedResponse;
}

This should get you going.

PFFile object does not contain method for downloading because it is built in functionality of iOS SDK. Or you can use AFNetworking as alternative. I think that the simplest way just to download the file is using synchronous constructor of NSData in conjunction with GCD:

dispatch_async(dispatch_get_global_queue(0), ^(){

   NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:file.url]];

   dispath_async(dispatch_get_main_queue(), ^(){

      // do you main UI thread stuff here
   });
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top