Can I create an NSURL that refers to in-memory NSData?
Asked Answered
F

2

34

The docs for NSURL state that:

An NSURL object represents a URL that can potentially contain the location of a resource on a remote server, the path of a local file on disk, or even an arbitrary piece of encoded data.

I have a blob of in-memory data that I'd like to hand to a library that wants to load a resource via an NSURL. Sure, I can first write this NSData to a temp file and then create a file:// NSURL from that, but I'd prefer to have the URL point directly to the buffer that I already have present in memory.

The docs quoted above seem to suggest this is possible, but I can't find any hint of how to accomplish it. Am I missing something?

Foxhole answered 14/5, 2014 at 1:25 Comment(4)
Maybe it means you can give it a Data URI? en.wikipedia.org/wiki/Data_URI_schemeMelliemelliferous
If I have a URL like - chart.googleapis.com/chart?cht=qr&chl=QRUPRC%3A it will return a barcode image to me, but that image is not really stored physically in google's file server - it is generated. I think that is what it meansHatchel
So basically you want [NSData dataWithContentsOfURL:myURLHere] to give the data that you already have in RAM instead of reading it from a file?Alric
The documentation for NSData makes a few references to data:// as a supported URL protocol, but I can't find any documentation for exactly how it works. Most likely it's just the data as a hex string?Alric
M
34

NSURL supports the data:// URL-Scheme (RFC 2397).
This scheme allows you to build URLs in the form of

data://data:MIME-Type;base64,<data>

A working Cocoa example would be:

NSImage* img = [NSImage imageNamed:@"img"];
NSData* imgData = [img TIFFRepresentation];
NSString* dataFormatString = @"data:image/png;base64,%@";
NSString* dataString = [NSString stringWithFormat:dataFormatString, [imgData base64EncodedStringWithOptions:0]];
NSURL* dataURL = [NSURL URLWithString:dataString];

Passing around large binary blobs with data URLs might be a bit inefficient due to the nature of base64 encoding.

You could also implement a custom NSURLProtocol that specifically deals with your data. Apple has some sample code that uses a custom protocol to pass around image objects: https://developer.apple.com/library/mac/samplecode/SpecialPictureProtocol/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003816

Madelle answered 14/5, 2014 at 6:30 Comment(3)
Another example: [NSString stringWithContentsOfURL:[NSURL URLWithString:@"data:text/plain;base64,aGVsbG8gd29ybGQ="] encoding:NSUTF8StringEncoding error:NULL]; gives you @"hello world". Not too inefficient, it only 4 extra bytes compared to ASCII.Alric
Thanks. The data: protocol is definitely functional for smaller buffers (if anyone's interested I wrote a tiny protocol that does it here: github.com/bzotto/NSURL-DataAdditions). The custom NSURLProtocol way is probably the "best" answer here, because the first option would be hugely wasteful in terms of memory (and requires a bunch of encoding) for anything nontrivial.Foxhole
I attempted to use this logic to create a NSURL with MP4 video data inline and then play that with AVFoundation. A movie file will not load using this approach, perhaps the NSURLProtocol would be more successful.Eb
T
6

What you are missing is the NSURLProtocol class. Takes about three dozen lines of code, and any code that handles URLs properly can access your in-memory data. Read the documentation, it's not difficult and there is sample code available.

Unfortunately there are some APIs that take an NSURL as a parameter, but can only handle file URLs.

Titustityus answered 26/11, 2014 at 1:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.