在 iPhone 上通过 ftp 在后台上传文件

发布于 2025-01-02 11:07:05 字数 1318 浏览 0 评论 0原文

我正在尝试在应用程序内建立 FTP 连接。我想将多个文件上传到 FTP 服务器,所有文件都在一个目录中。所以首先我想创建远程目录。

- (void) createRemoteDir {

    NSURL *destinationDirURL = [NSURL URLWithString: uploadDir];

    CFWriteStreamRef writeStreamRef = CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) destinationDirURL);
    assert(writeStreamRef != NULL);

    ftpStream = (__bridge_transfer NSOutputStream *) writeStreamRef;
    BOOL success = [ftpStream setProperty: ftpUser forKey: (id)kCFStreamPropertyFTPUserName];
    if (success) {
        NSLog(@"\tsuccessfully set the user name");
    }
    success = [ftpStream setProperty: ftpPass forKey: (id)kCFStreamPropertyFTPPassword];
    if (success) {
        NSLog(@"\tsuccessfully set the password");
    }

    ftpStream.delegate = self;
    [ftpStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    // open stream
    [ftpStream open];
}

当使用以下调用在后台线程中执行时,此代码不起作用:

[self performSelectorInBackground: @selector(createRemoteDir) withObject: nil];

我的猜测是(后台线程)运行循环没有运行? 如果我在主线程内发送消息,则上传工作正常:

[self createRemoteDir];

因为主线程的运行循环已启动并正在运行。

但要上传相当大的文件;所以我想将该工作负载放在后台线程中。 但是我如何以及在哪里设置 NSRunLoop,以便整个上传发生在后台线程中?苹果关于 NSRunLoops 的文档(特别是如何在不使用计时器/输入源的情况下启动它们,如本例所示)并没有帮助我。

i'm trying to establish an FTP connection within an app. i want to upload several files to a FTP server, all files in one directory. So at first i want to create the remote directory.

- (void) createRemoteDir {

    NSURL *destinationDirURL = [NSURL URLWithString: uploadDir];

    CFWriteStreamRef writeStreamRef = CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) destinationDirURL);
    assert(writeStreamRef != NULL);

    ftpStream = (__bridge_transfer NSOutputStream *) writeStreamRef;
    BOOL success = [ftpStream setProperty: ftpUser forKey: (id)kCFStreamPropertyFTPUserName];
    if (success) {
        NSLog(@"\tsuccessfully set the user name");
    }
    success = [ftpStream setProperty: ftpPass forKey: (id)kCFStreamPropertyFTPPassword];
    if (success) {
        NSLog(@"\tsuccessfully set the password");
    }

    ftpStream.delegate = self;
    [ftpStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    // open stream
    [ftpStream open];
}

This code doesn't work when executed in a background thread using the following call:

[self performSelectorInBackground: @selector(createRemoteDir) withObject: nil];

my guess is that the (background-threads) runloop isn't running?
If i send the message inside the main thread the uploading just works fine:

[self createRemoteDir];

as the runloop of the main thread is up and running.

but fairly large files are going to be uploaded; so i want to put that workload in a background thread.
but how and where do i set up the NSRunLoop, so that the whole uploading happens in a background thread? Apples documentation on NSRunLoops (especially how to start them without using a timer/input source, as in this case) didn't help me out.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

回忆躺在深渊里 2025-01-09 11:07:05

我找到/创建了一个至少对我有用的解决方案。
使用上述方法(createRemoteDir),以下代码适用于我并为我工作:

NSError *error;

createdDirectory = FALSE;
/* 
 only 'prepares' the stream for upload 
 - doesn't actually upload anything until the runloop of this background thread is run
 */
[self createRemoteDir];

NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];

do {

    if(![currentRunLoop runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]]) {

        // log error if the runloop invocation failed
        error = [[NSError alloc] initWithDomain: @"org.mJae.FTPUploadTrial" 
                                           code: 23 
                                       userInfo: nil];
    }

} while (!createdDirectory && !error);

// close stream, remove from runloop
[ftpStream close];
[ftpStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];

if (error) {
    // handle error
}

它在后台线程中运行并在 ftp 服务器上创建目录。
与其他示例相比,我更喜欢它,在这些示例中,运行循环仅运行一个假定的小间隔(例如 1 秒)。

[NSDate distantFuture]

是未来的日期(根据苹果文档,是几个世纪)。但这很好,因为“中断条件”由我的类属性 createdDirectory 处理 - 或者在启动运行循环时发生错误。

我无法解释为什么它在没有我明确地将输入源附加到运行循环(NSTimer 或 NSPort)的情况下工作,但我的猜测是,在后台线程的运行循环中调度 NSOutputStream 就足够了(参见 createRemoteDir )。

I found/created a solution that at least works for me.
with the above method (createRemoteDir), the following code applied and worked for me:

NSError *error;

createdDirectory = FALSE;
/* 
 only 'prepares' the stream for upload 
 - doesn't actually upload anything until the runloop of this background thread is run
 */
[self createRemoteDir];

NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];

do {

    if(![currentRunLoop runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]]) {

        // log error if the runloop invocation failed
        error = [[NSError alloc] initWithDomain: @"org.mJae.FTPUploadTrial" 
                                           code: 23 
                                       userInfo: nil];
    }

} while (!createdDirectory && !error);

// close stream, remove from runloop
[ftpStream close];
[ftpStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];

if (error) {
    // handle error
}

It runs in a background thread and creates the directory on the ftp server.
I like it more than other examples where runloops are only run for an assumed small interval, say 1second.

[NSDate distantFuture]

is a date in the futur (several centuries, according to Apples documentation). But that's good as the "break-condition" is handled by my class property createdDirectory - or the occurance of an error while starting the runloop.

I can't explain why it works without me explicitly attaching an input source to the runloop (NSTimer or NSPort), but my guess is, it is sufficient that the NSOutputStream is scheduled in the runloop of the background thread (see createRemoteDir).

十二 2025-01-09 11:07:05

您还可以尝试使用dispatch_async调用在后台执行createRemoteDir。它使用起来更简单,您不必担心管理额外的线程。

代码如下所示:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self createRemoteDir];
});

You could also try to use a dispatch_async call to perform your createRemoteDir in the background. It's much simpler to use and you won't have to worry about managing extra threads.

Here's what the code would look like:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self createRemoteDir];
});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文