Skip to content

Commit

Permalink
Add Image.getSizeWithHeaders (#18850)
Browse files Browse the repository at this point in the history
Summary:
This adds new functionality to the `Image` component by allowing you to retrieve the width and height of an image just like you'd do with [`Image.getSize`](https://facebook.github.io/react-native/docs/image.html#getsize) but _with_ the ability to provide headers to your request.

Why would you need this you ask? Well, imagine that you have an image that you're loading into your `Image` component that is protected and you get access by using a token in a header (or something similar). That would work. However, getting the dimensions isn't possible since you can't provide those same headers.
This is something that is bothering me when using a third-party library (https://github.com/archriss/react-native-image-gallery) and instead of implementing this just for that single library I imagined that it would be useful for anyone else that needs to get the image dimensions before displaying it.

[Android] [Added] - Added Image.getSizeWithHeaders
[iOS] [Added] - Added Image.getSizeWithHeaders
Pull Request resolved: #18850

Differential Revision: D14434599

Pulled By: cpojer

fbshipit-source-id: 56d5e58889ddf7ddc12d5f6f7d9dc6921fa17884
  • Loading branch information
se-bastiaan authored and facebook-github-bot committed Mar 13, 2019
1 parent 8270de9 commit c991e1c
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 0 deletions.
41 changes: 41 additions & 0 deletions Libraries/Image/Image.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ const ImageProps = {
]),
};

/**
* Retrieve the width and height (in pixels) of an image prior to displaying it
*
* See https://facebook.github.io/react-native/docs/image.html#getsize
*/
function getSize(
url: string,
success: (width: number, height: number) => void,
Expand All @@ -140,6 +145,30 @@ function getSize(
);
}

/**
* Retrieve the width and height (in pixels) of an image prior to displaying it
* with the ability to provide the headers for the request
*
* See https://facebook.github.io/react-native/docs/image.html#getsizewithheaders
*/
function getSizeWithHeaders(
url: string,
headers: {[string]: string},
success: (width: number, height: number) => void,
failure?: (error: any) => void,
) {
return ImageLoader.getSizeWithHeaders(url, headers)
.then(function(sizes) {
success(sizes.width, sizes.height);
})
.catch(
failure ||
function() {
console.warn('Failed to get size for image: ' + url);
},
);
}

function prefetch(url: string, callback: ?Function) {
const requestId = generateRequestId();
callback && callback(requestId);
Expand All @@ -165,6 +194,7 @@ declare class ImageComponentType extends ReactNative.NativeComponent<
ImagePropsType,
> {
static getSize: typeof getSize;
static getSizeWithHeaders: typeof getSizeWithHeaders;
static prefetch: typeof prefetch;
static abortPrefetch: typeof abortPrefetch;
static queryCache: typeof queryCache;
Expand Down Expand Up @@ -271,6 +301,17 @@ Image.displayName = 'Image';
* comment and run Flow. */
Image.getSize = getSize;

/**
* Retrieve the width and height (in pixels) of an image prior to displaying it
* with the ability to provide the headers for the request
*
* See https://facebook.github.io/react-native/docs/image.html#getsizewithheaders
*/
/* $FlowFixMe(>=0.89.0 site=react_native_android_fb) This comment suppresses an
* error found when Flow v0.89 was deployed. To see the error, delete this
* comment and run Flow. */
Image.getSizeWithHeaders = getSizeWithHeaders;

/**
* Prefetches a remote image for later use by downloading it to the disk
* cache
Expand Down
30 changes: 30 additions & 0 deletions Libraries/Image/Image.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ function getSize(
);
}

function getSizeWithHeaders(
uri: string,
headers: {[string]: string},
success: (width: number, height: number) => void,
failure?: (error: any) => void,
) {
return ImageViewManager.getSizeWithHeaders({uri, headers})
.then(function(sizes) {
success(sizes.width, sizes.height);
})
.catch(
failure ||
function() {
console.warn('Failed to get size for image: ' + uri);
},
);
}

function prefetch(url: string) {
return ImageViewManager.prefetchImage(url);
}
Expand All @@ -56,6 +74,7 @@ declare class ImageComponentType extends ReactNative.NativeComponent<
ImagePropsType,
> {
static getSize: typeof getSize;
static getSizeWithHeaders: typeof getSizeWithHeaders;
static prefetch: typeof prefetch;
static queryCache: typeof queryCache;
static resolveAssetSource: typeof resolveAssetSource;
Expand Down Expand Up @@ -136,6 +155,17 @@ Image.displayName = 'Image';
* comment and run Flow. */
Image.getSize = getSize;

/**
* Retrieve the width and height (in pixels) of an image prior to displaying it
* with the ability to provide the headers for the request.
*
* See https://facebook.github.io/react-native/docs/image.html#getsizewithheaders
*/
/* $FlowFixMe(>=0.89.0 site=react_native_ios_fb) This comment suppresses an
* error found when Flow v0.89 was deployed. To see the error, delete this
* comment and run Flow. */
Image.getSizeWithHeaders = getSizeWithHeaders;

/**
* Prefetches a remote image for later use by downloading it to the disk
* cache.
Expand Down
15 changes: 15 additions & 0 deletions Libraries/Image/RCTImageViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import <UIKit/UIKit.h>

#import <React/RCTConvert.h>
#import <React/RCTImageSource.h>

#import "RCTImageLoader.h"
#import "RCTImageShadowView.h"
Expand Down Expand Up @@ -63,6 +64,20 @@ - (UIView *)view
}];
}

RCT_EXPORT_METHOD(getSizeWithHeaders:(RCTImageSource *)source
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
[self.bridge.imageLoader getImageSizeForURLRequest:source.request
block:^(NSError *error, CGSize size) {
if (error) {
reject(@"E_GET_SIZE_FAILURE", nil, error);
return;
}
resolve(@{@"width":@(size.width),@"height":@(size.height)});
}];
}

RCT_EXPORT_METHOD(prefetchImage:(NSURLRequest *)request
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ rn_android_library(
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/modules/fresco:fresco"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.facebook.react.modules.fresco.ReactNetworkImageRequest;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.GuardedAsyncTask;
import com.facebook.react.bridge.LifecycleEventListener;
Expand All @@ -30,6 +31,7 @@
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;

Expand Down Expand Up @@ -119,6 +121,67 @@ protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> data
dataSource.subscribe(dataSubscriber, CallerThreadExecutor.getInstance());
}

/**
* Fetch the width and height of the given image with headers.
*
* @param uriString the URI of the remote image to prefetch
* @param headers headers send with the request
* @param promise the promise that is fulfilled when the image is successfully prefetched
* or rejected when there is an error
*/
@ReactMethod
public void getSizeWithHeaders(
final String uriString,
final ReadableMap headers,
final Promise promise) {
if (uriString == null || uriString.isEmpty()) {
promise.reject(ERROR_INVALID_URI, "Cannot get the size of an image for an empty URI");
return;
}

Uri uri = Uri.parse(uriString);
ImageRequestBuilder imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(uri);
ImageRequest request = ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, headers);

DataSource<CloseableReference<CloseableImage>> dataSource =
Fresco.getImagePipeline().fetchDecodedImage(request, mCallerContext);

DataSubscriber<CloseableReference<CloseableImage>> dataSubscriber =
new BaseDataSubscriber<CloseableReference<CloseableImage>>() {
@Override
protected void onNewResultImpl(
DataSource<CloseableReference<CloseableImage>> dataSource) {
if (!dataSource.isFinished()) {
return;
}
CloseableReference<CloseableImage> ref = dataSource.getResult();
if (ref != null) {
try {
CloseableImage image = ref.get();

WritableMap sizes = Arguments.createMap();
sizes.putInt("width", image.getWidth());
sizes.putInt("height", image.getHeight());

promise.resolve(sizes);
} catch (Exception e) {
promise.reject(ERROR_GET_SIZE_FAILURE, e);
} finally {
CloseableReference.closeSafely(ref);
}
} else {
promise.reject(ERROR_GET_SIZE_FAILURE);
}
}

@Override
protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
promise.reject(ERROR_GET_SIZE_FAILURE, dataSource.getFailureCause());
}
};
dataSource.subscribe(dataSubscriber, CallerThreadExecutor.getInstance());
}

/**
* Prefetches the given image to the Fresco image disk cache.
*
Expand Down

0 comments on commit c991e1c

Please sign in to comment.