Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This pull request adds support for loading PDF documents from original React Native's Blob via object URL.
An example use case of this feature is decrypting PDF documents and storing the decrypted data in memory as a
Blob
instead of saving the file on the device's filesystem, which may be not desirable due to security reasons.Example
Sample blob object URLs:
content://com.example.blobs/48fa2c0e-50ad-437c-a60a-5920292cb850?offset=0&size=22092
blob:F9B2C33F-55E7-4831-A0A7-EB95CC6F8DFF?offset=0&size=22092
Note: On Android, it is necessary to register
BlobProvider
as a ContentProvider in order to create object URLs for blobs (see details). On iOS, there is no configuration required.Current behaviour
Android
Passing a content URI as
<Pdf />
component source on Android causes the following error:[Error: File is empty]
.This exception is thrown in native implementation of
nativeOpenDocument
method in PdfiumAndroid (see the code). On Android, BlobProvider returns the file descriptor to read side of a pipe. PdfiumAndroid callsfstat
to check the file size (see the code). For a pipe,st_size
is zero (see https://yarchive.net/comp/linux/stat_size.html).This issue has been reproduced using
test.pdf
document which is smaller than 64 KB due to an issue with BlobProvider when accessing blobs larger than pipe capacity (see issue facebook/react-native#31774 and pull request facebook/react-native#31789). This will be fixed in React Native 0.65.0.iOS
Passing a blob URL as
<Pdf />
component source causes the following error:[Error: Load pdf failed. path=blob:1B035D9C-B720-48F9-B79D-8648B6153D35?offset=0&size=22092]
.react-native-pdf/ios/RCTPdf/RCTPdfView.m
Line 140 in ab11418
Instantiating
PDFDocument
from PDFKit usinginitWithURL
method fails, because the passed blob URL is not a valid file URL to an existing file, thus_pdfDocument
isNil
.react-native-pdf/ios/RCTPdf/RCTPdfView.m
Line 124 in ab11418
Changes
Android
The new implementation uses
ContentResolver.openInputStream
method to resolve blob from the provided content URI and then passes the input stream directly toConfigurator.fromStream
method. This happens only if the provided URL is prefixed withcontent://
.iOS
Because there is no content provider mechanism on iOS, the new implementation simply accesses the BlobModule and retrieves the blob data using
resolveURL
method. TheNSURL
object is instantiated usingURLWithString
method instead offileURLWithPath
. This happens only if the provided URL is prefixed withblob:
.Further improvements
Android
While the suggested approach is to make use of ContentProvider mechanism (which is similar to what React Native's ImageView does), it has two major drawbacks. First, it will not work properly on older versions of React Native (without facebook/react-native#31789 fix) as the vast majority of PDFs is larger than 64 KB. Another drawback is that native code cannot use Java streams, so the input stream from ContentProvider is actually copied to a new bytearray (see here, here and here).
Instead, we could get blob data directly from
BlobProvider
usingresolve
method (just like here), analogously as for iOS, and then callConfigurator.fromBytes
method. This approach would eliminate the need for copying the bytearray (see here, here and here).Additionally, this approach does not require patching React Native as it completely avoids BlobProvider.
Another minor improvement would be to use
BLOB_URI_SCHEME
(from here) instead of hard-coding thecontent://
prefix.iOS
Using
BLOB_URI_SCHEME
from here instead of the hard-codedblob:
prefix would be a minor improvement.Testing
Due to issue with blobs larger than 64 KB on Android, the example app has been tested with
test.pdf
document fetched as blob from Python HTTP server which can be started with the following command: