Dailycode.info

Short solution for short problems

iOS : Save file to windows file share.

I had tried several solutions. The first that really worked stable was to use http post to send the data to a php recieving file. The solution can be found here. But that was hard to control and it fails when files get bigger then 2Mb. It was also relative slow. 

So my current solution works fantastic!!! I'm using FTP now to send files from my iOS device to the Windows server. The only thing you need to do on server side is to enable ftp and create and ftp site. I also used basic authentication with an active directory user. 

On iOS site I followed this example from apple themselves and create a ViewController that can handle all your uploading. Based on file path or NData and file name. You can simply call the class like this:

 

self.uploadController.parentView = self.view;

self.uploadController.dataToSend = imageData;

self.uploadController.fileName = @"MyFile.PNG";

[self.uploadController UploadPressed:sender];

 

First we tell which parent view to use. This is to show the progress of the file upload.

Then we provide the NSData which can be read from image or video or any other kind of resource. Here you get an example of a picture that is on an image view:

 

NSData *imageData = UIImagePNGRepresentation(self.imageView.image);

self.uploadController.dataToSend = imageData;

 

And here an example of a video based on the video's url on the device:

 

NSData *videoData;

if(self.videoUrl)

{

      videoData = [NSData dataWithContentsOfURL:self.videoUrl];

}

        

if (videoData)

{

      self.uploadController.dataToSend = videoData;

      ...

 

So thats really it. It will send your files to the url which you had defined in your App using the user and password. Now here's the ViewController that does all the work for you:

The UploadVC.h

//

//  UploadV3

//  TestBARCodeAndSIngnature

//

//  Created by Mark Deraeve on 28/01/13.

//  Copyright (c) 2013. All rights reserved.

//

 

#import <UIKit/UIKit.h>

#include <CFNetwork/CFNetwork.h>

 

@interface UploadVC : UIViewController <NSStreamDelegate>

 

@property (nonatomicassignreadonly ) BOOL              isSending;

@property (nonatomicstrongreadwriteNSOutputStream *  networkStream;

@property (nonatomicstrongreadwriteNSInputStream *   fileStream;

@property (nonatomicassignreadonly ) uint8_t *         buffer;

@property (nonatomicassignreadwritesize_t            bufferOffset;

@property (nonatomicassignreadwritesize_t            bufferLimit;

@property (nonatomicstrongNSData * dataToSend;

@property (nonatomicstrongNSString * fileName;

@property (nonatomicstrongNSString * filePath;

@property (nonatomicstrongUIView * parentView;

 

- (void)UploadPressed:(id)sender;

@end

The UploadVC.m

//

//  UploadV3

//  TestBARCodeAndSIngnature

//

//  Created by Mark Deraeve on 28/01/13.

//  Copyright (c) 2013. All rights reserved.

//

 

#import "UploadVC.h"

#import "NetworkManager.h"

#import "VariableStore.h"

#import "GeneralMESFunctions.h"

 

 

enum {

    kSendBufferSize = 32768

};

 

@interfaceUploadVC ()

 

 

 

@end

 

@implementation UploadVC

{

    uint8_t                     _buffer[kSendBufferSize];

}

 

VariableStore * store;

MBProgressHUD * hud;

 

- (void)sendDidStart

{

    [[NetworkManagersharedInstancedidStartNetworkOperation];

}

 

- (void)updateStatus:(NSString *)statusString

{

    assert(statusString != nil);

}

 

- (void)sendDidStopWithStatus:(NSString *)statusString

{

    if (statusString == nil) {

        statusString = @"Put succeeded";

    }

    [[NetworkManagersharedInstancedidStopNetworkOperation];

}

 

#pragma mark * Core transfer code

 

// This is the code that actually does the networking.

 

// Because buffer is declared as an array, you have to use a custom getter.

// A synthesised getter doesn't compile.

 

- (uint8_t *)buffer

{

    returnself->_buffer;

}

 

- (BOOL)isSending

{

    return (self.networkStream != nil);

}

 

- (void)startSend:(NSString *) filePath

{

    BOOL                    success;

    NSURL *                 url;

    hud = [GeneralMESFunctionsGetMBProgressHUDForView:self.parentView];

    hud.detailsLabelText = @"Uploading file...";

 

    assert(filePath != nil);

    assert([[NSFileManagerdefaultManagerfileExistsAtPath:filePath]);

    assert( [filePath.pathExtensionisEqual:@"png"] || [filePath.pathExtensionisEqual:@"jpg"] );

    

    assert(self.networkStream == nil);      // don't tap send twice in a row!

    assert(self.fileStream == nil);         // ditto

    

    // First get and check the URL.

    

    url = [[NetworkManagersharedInstancesmartURLForString:store.MESFTPPath];

    success = (url != nil);

    

    if (success) {

        // Add the last part of the file name to the end of the URL to form the final

        // URL that we're going to put to.

        

        url = CFBridgingRelease(

                                CFURLCreateCopyAppendingPathComponent(NULL, (__bridge CFURLRef) url, (__bridge CFStringRef) [filePath lastPathComponent], false)

                                );

        success = (url != nil);

    }

    

    // If the URL is bogus, let the user know.  Otherwise kick off the connection.

    

    if ( ! success) {

        //self.txtStatus.text = @"Invalid URL";

    } else {

        

        // Open a stream for the file we're going to send.  We do not open this stream;

        // NSURLConnection will do it for us.

        

        self.fileStream = [NSInputStream inputStreamWithFileAtPath:filePath];

        assert(self.fileStream != nil);

        

        [self.fileStream open];

        

        // Open a CFFTPStream for the URL.

        

        self.networkStream = CFBridgingRelease(

                                               CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) url)

                                               );

        assert(self.networkStream != nil);

        

        if ([store.MESFTPUser length] != 0) {

            success = [self.networkStream setProperty:store.MESFTPUser forKey:(id)kCFStreamPropertyFTPUserName];

            assert(success);

            success = [self.networkStream setProperty:store.MESFTPPWD forKey:(id)kCFStreamPropertyFTPPassword];

            assert(success);

        }

        

        self.networkStream.delegate = self;

        [self.networkStreamscheduleInRunLoop:[NSRunLoopcurrentRunLoopforMode:NSDefaultRunLoopMode];

        [self.networkStream open];

        

        // Tell the UI we're sending.

        

        [self sendDidStart];

    }

}

 

- (void)startSend:(NSData *) dataToSend fileName:(NSString *) fileName

{

    BOOL                    success;

    NSURL *                 url;

    

    hud = [GeneralMESFunctionsGetMBProgressHUDForView:self.parentView];

    hud.detailsLabelText = @"Uploading file...";

    

    assert(dataToSend != nil);

    assert(self.networkStream == nil);      // don't tap send twice in a row!

    assert(self.fileStream == nil);         // ditto

    

    // First get and check the URL.

    

    url = [[NetworkManagersharedInstancesmartURLForString:store.MESFTPPath];

    success = (url != nil);

    

    if (success) {

        // Add the last part of the file name to the end of the URL to form the final

        // URL that we're going to put to.

        

        url = CFBridgingRelease(

                                CFURLCreateCopyAppendingPathComponent(NULL, (__bridge CFURLRef) url, (__bridge CFStringRef) fileName, false)

                                );

        success = (url != nil);

    }

    

    // If the URL is bogus, let the user know.  Otherwise kick off the connection.

    

    if ( ! success) {

        //self.txtStatus.text = @"Invalid URL";

    } else {

        

        // Open a stream for the file we're going to send.  We do not open this stream;

        // NSURLConnection will do it for us.

        

        self.fileStream = [NSInputStream inputStreamWithData:dataToSend];

        assert(self.fileStream != nil);

        

        [self.fileStream open];

        

        // Open a CFFTPStream for the URL.

        

        self.networkStream = CFBridgingRelease(

                                               CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) url)

                                               );

        assert(self.networkStream != nil);

        

        if ([store.MESFTPUser length] != 0) {

            success = [self.networkStream setProperty:store.MESFTPUser forKey:(id)kCFStreamPropertyFTPUserName];

            assert(success);

            success = [self.networkStream setProperty:store.MESFTPPWD forKey:(id)kCFStreamPropertyFTPPassword];

            assert(success);

        }

        

        self.networkStream.delegate = self;

        [self.networkStreamscheduleInRunLoop:[NSRunLoopcurrentRunLoopforMode:NSDefaultRunLoopMode];

        [self.networkStream open];

        

        // Tell the UI we're sending.

        

        [self sendDidStart];

    }

}

 

- (void)stopSendWithStatus:(NSString *)statusString

{

    if (self.networkStream != nil) {

        [self.networkStreamremoveFromRunLoop:[NSRunLoopcurrentRunLoopforMode:NSDefaultRunLoopMode];

        self.networkStream.delegate = nil;

        [self.networkStream close];

        self.networkStream = nil;

    }

    if (self.fileStream != nil) {

        [self.fileStream close];

        self.fileStream = nil;

    }

    [MBProgressHUDhideHUDForView:self.parentViewanimated:YES];

    [self sendDidStopWithStatus:statusString];

}

 

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode

// An NSStream delegate callback that's called when events happen on our

// network stream.

{

#pragma unused(aStream)

    assert(aStream == self.networkStream);

    

    switch (eventCode) {

        caseNSStreamEventOpenCompleted: {

            [selfupdateStatus:@"Opened connection"];

        } break;

        caseNSStreamEventHasBytesAvailable: {

            assert(NO);     // should never happen for the output stream

        } break;

        caseNSStreamEventHasSpaceAvailable: {

            [self updateStatus:@"Sending"];

            

            // If we don't have any data buffered, go read the next chunk of data.

            

            if (self.bufferOffset == self.bufferLimit) {

                NSInteger   bytesRead;

                

                bytesRead = [self.fileStream read:self.buffer maxLength:kSendBufferSize];

                

                if (bytesRead == -1) {

                    [self stopSendWithStatus:@"File read error"];

                } else if (bytesRead == 0) {

                    [self stopSendWithStatus:nil];

                } else {

                    self.bufferOffset = 0;

                    self.bufferLimit  = bytesRead;

                }

            }

            

            // If we're not out of data completely, send the next chunk.

            

            if (self.bufferOffset != self.bufferLimit) {

                NSInteger   bytesWritten;

                bytesWritten = [self.networkStreamwrite:&self.buffer[self.bufferOffsetmaxLength:self.bufferLimit - self.bufferOffset];

                assert(bytesWritten != 0);

                if (bytesWritten == -1) {

                    [self stopSendWithStatus:@"Network write error"];

                } else {

                    self.bufferOffset += bytesWritten;

                }

            }

        } break;

        caseNSStreamEventErrorOccurred: {

            [selfstopSendWithStatus:@"Stream open error"];

        } break;

        caseNSStreamEventEndEncountered: {

            // ignore

        } break;

        default: {

            assert(NO);

        } break;

    }

}

 

- (void)viewDidLoad

{

    [superviewDidLoad];

// Do any additional setup after loading the view, typically from a

}

 

- (void)didReceiveMemoryWarning

{

    [superdidReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

- (void)UploadPressed:(id)sender;

{

    store = [VariableStoresharedInstance];

    if (self.dataToSend && self.fileName)

    {

        [selfstartSend:self.dataToSendfileName:self.fileName];

    }

    else

    {

        [self startSend:self.filePath];

    }

}

 

@end

 

As you can see, you're missing some files. I use a simple progress window MBProgressHUD, for better user experience, but you can loose this. You could also use the activity view. Then I get my settings from a class called Variable store. This is the place were I put all my shared variables used throughout the program. So there I get the url, user and password for the file share. This you can also put hardcoded or get is from your own singleton class. First test are very good. Tested with large video files and pictures.  The last file you will need is the NetWorkManager written by apple:

NetworkManager.h:

 

/*

 File:       NetworkManager.h

 

 Contains:   Shared state and utilities for networking.

 

 Written by: DTS

 

 Copyright:  Copyright (c) 2009-2012 Apple Inc. All Rights Reserved.

 

 */

 

#import <Foundation/Foundation.h>

 

@interface NetworkManager : NSObject

 

+ (NetworkManager *)sharedInstance;

 

- (NSURL *)smartURLForString:(NSString *)str;

- (BOOL)isImageURL:(NSURL *)url;

 

@property (nonatomicassignreadonly ) NSUInteger     networkOperationCount;  // observable

 

- (void)didStartNetworkOperation;

- (void)didStopNetworkOperation;

 

@end

 
NetworkManager.m
 

/*

 File:       NetworkManager.m

 

 Contains:   Shared state and utilities for networking.

 

 Written by: DTS

 

 Copyright:  Copyright (c) 2009-2012 Apple Inc. All Rights Reserved.

 

 */

 

#import "NetworkManager.h"

 

@interface NetworkManager ()

 

// read/write redeclaration of public read-only property

 

@property (nonatomicassignreadwriteNSUInteger     networkOperationCount;

 

@end

 

@implementation NetworkManager

 

@synthesize networkOperationCount = _networkOperationCount;

 

+ (NetworkManager *)sharedInstance

{

    static dispatch_once_t  onceToken;

    static NetworkManager * sSharedInstance;

    

    dispatch_once(&onceToken, ^{

        sSharedInstance = [[NetworkManager allocinit];

    });

    return sSharedInstance;

}

 

- (NSURL *)smartURLForString:(NSString *)str

{

    NSURL *     result;

    NSString *  trimmedStr;

    NSRange     schemeMarkerRange;

    NSString *  scheme;

    

    assert(str != nil);

    

    result = nil;

    

    trimmedStr = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

    if ( (trimmedStr != nil) && ([trimmedStr length] != 0) ) {

        schemeMarkerRange = [trimmedStr rangeOfString:@"://"];

        

        if (schemeMarkerRange.location == NSNotFound) {

            result = [NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@", trimmedStr]];

        } else {

            scheme = [trimmedStr substringWithRange:NSMakeRange(0, schemeMarkerRange.location)];

            assert(scheme != nil);

            

            if ( ([scheme compare:@"ftp"  options:NSCaseInsensitiveSearch] == NSOrderedSame) ) {

                result = [NSURL URLWithString:trimmedStr];

            } else {

                // It looks like this is some unsupported URL scheme.

            }

        }

    }

    

    return result;

}

 

- (BOOL)isImageURL:(NSURL *)url

{

    BOOL        result;

    NSString *  extension;

    

    assert(url != nil);

    

    extension = [url pathExtension];

    result = NO;

    if (extension != nil) {

        result = ([extension caseInsensitiveCompare:@"gif"] == NSOrderedSame)

        || ([extension caseInsensitiveCompare:@"png"] == NSOrderedSame)

        || ([extension caseInsensitiveCompare:@"jpg"] == NSOrderedSame);

    }

    return result;

}

 

- (void)didStartNetworkOperation

{

    // If you start a network operation off the main thread, you'll have to update this code

    // to ensure that any observers of this property are thread safe.

    assert([NSThread isMainThread]);

    self.networkOperationCount += 1;

}

 

- (void)didStopNetworkOperation

{

    // If you stop a network operation off the main thread, you'll have to update this code

    // to ensure that any observers of this property are thread safe.

    assert([NSThread isMainThread]);

    assert(self.networkOperationCount > 0);

    self.networkOperationCount -= 1;

}

 

@end

 
I removed some code from this file that was related to their demo project and not nice for general purpose use.