Fix Android sharing files without extension (#1646)

This commit is contained in:
Elias Nahum
2018-04-30 11:57:24 -03:00
committed by GitHub
parent 8a152ac1e7
commit aebe22f58d
4 changed files with 89 additions and 4 deletions

View File

@@ -7,6 +7,7 @@ import android.os.Build;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.ContentResolver;
import android.os.Environment; import android.os.Environment;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
@@ -98,6 +99,7 @@ public class RealPathUtil {
cacheDir.mkdirs(); cacheDir.mkdirs();
} }
String mimeType = getMimeType(uri.getPath());
tmpFile = File.createTempFile("tmp", fileName, cacheDir); tmpFile = File.createTempFile("tmp", fileName, cacheDir);
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r"); ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
@@ -182,6 +184,11 @@ public class RealPathUtil {
return getMimeType(file); return getMimeType(file);
} }
public static String getMimeTypeFromUri(final Context context, final Uri uri) {
ContentResolver cR = context.getContentResolver();
return cR.getType(uri);
}
public static void deleteTempFiles(final File dir) { public static void deleteTempFiles(final File dir) {
try { try {
if (dir.isDirectory()) { if (dir.isDirectory()) {

View File

@@ -126,7 +126,7 @@ public class ShareModule extends ReactContextBaseJavaModule {
map = Arguments.createMap(); map = Arguments.createMap();
text = "file://" + filePath; text = "file://" + filePath;
map.putString("value", text); map.putString("value", text);
map.putString("type", RealPathUtil.getMimeType(filePath)); map.putString("type", RealPathUtil.getMimeTypeFromUri(currentActivity, uri));
items.pushMap(map); items.pushMap(map);
} }
} }

View File

@@ -3,11 +3,13 @@
import {Platform} from 'react-native'; import {Platform} from 'react-native';
import RNFetchBlob from 'react-native-fetch-blob'; import RNFetchBlob from 'react-native-fetch-blob';
import mimeDB from 'mime-db';
import {lookupMimeType} from 'mattermost-redux/utils/file_utils'; import {lookupMimeType} from 'mattermost-redux/utils/file_utils';
import {DeviceTypes} from 'app/constants/'; import {DeviceTypes} from 'app/constants/';
const EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
const {DOCUMENTS_PATH, IMAGES_PATH, VIDEOS_PATH} = DeviceTypes; const {DOCUMENTS_PATH, IMAGES_PATH, VIDEOS_PATH} = DeviceTypes;
const DEFAULT_SERVER_MAX_FILE_SIZE = 50 * 1024 * 1024;// 50 Mb const DEFAULT_SERVER_MAX_FILE_SIZE = 50 * 1024 * 1024;// 50 Mb
@@ -32,6 +34,9 @@ const SUPPORTED_VIDEO_FORMAT = Platform.select({
android: ['video/3gpp', 'video/x-matroska', 'video/mp4', 'video/webm'], android: ['video/3gpp', 'video/x-matroska', 'video/mp4', 'video/webm'],
}); });
const types = {};
const extensions = {};
export function generateId() { export function generateId() {
// Implementation taken from http://stackoverflow.com/a/2117523 // Implementation taken from http://stackoverflow.com/a/2117523
let id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; let id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
@@ -144,3 +149,69 @@ export const isVideo = (file) => {
return SUPPORTED_VIDEO_FORMAT.includes(mime); return SUPPORTED_VIDEO_FORMAT.includes(mime);
}; };
/**
* Get the default extension for a MIME type.
*
* @param {string} type
* @return {boolean|string}
*/
export function getExtensionFromMime(type) {
if (!Object.keys(extensions).length) {
populateMaps();
}
if (!type || typeof type !== 'string') {
return false;
}
// TODO: use media-typer
const match = EXTRACT_TYPE_REGEXP.exec(type);
// get extensions
const exts = match && extensions[match[1].toLowerCase()];
if (!exts || !exts.length) {
return false;
}
return exts[0];
}
/**
* Populate the extensions and types maps.
* @private
*/
function populateMaps() {
// source preference (least -> most)
const preference = ['nginx', 'apache', undefined, 'iana']; //eslint-disable-line no-undefined
Object.keys(mimeDB).forEach((type) => {
const mime = mimeDB[type];
const exts = mime.extensions;
if (!exts || !exts.length) {
return;
}
extensions[type] = exts;
for (let i = 0; i < exts.length; i++) {
const extension = exts[i];
if (types[extension]) {
const from = preference.indexOf(mimeDB[types[extension]].source);
const to = preference.indexOf(mime.source);
if (types[extension] !== 'application/octet-stream' &&
(from > to || (from === to && types[extension].substr(0, 12) === 'application/'))) {
continue;
}
}
types[extension] = type;
}
});
}

View File

@@ -6,6 +6,7 @@ import {NavigationActions} from 'react-navigation';
import TouchableItem from 'react-navigation/src/views/TouchableItem'; import TouchableItem from 'react-navigation/src/views/TouchableItem';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {intlShape} from 'react-intl'; import {intlShape} from 'react-intl';
import { import {
Image, Image,
NativeModules, NativeModules,
@@ -25,6 +26,7 @@ import {getFormattedFileSize, lookupMimeType} from 'mattermost-redux/utils/file_
import PaperPlane from 'app/components/paper_plane'; import PaperPlane from 'app/components/paper_plane';
import mattermostManaged from 'app/mattermost_managed'; import mattermostManaged from 'app/mattermost_managed';
import {getExtensionFromMime} from 'app/utils/file';
import {emptyFunction} from 'app/utils/general'; import {emptyFunction} from 'app/utils/general';
import {preventDoubleTap} from 'app/utils/tap'; import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
@@ -278,15 +280,20 @@ export default class ExtensionPost extends PureComponent {
break; break;
default: { default: {
const fullPath = item.value; const fullPath = item.value;
const filename = fullPath.replace(/^.*[\\/]/, '');
const extension = filename.split('.').pop();
const fileSize = await RNFetchBlob.fs.stat(fullPath); const fileSize = await RNFetchBlob.fs.stat(fullPath);
let filename = fullPath.replace(/^.*[\\/]/, '');
let extension = filename.split('.').pop();
if (extension === filename) {
extension = getExtensionFromMime(item.type);
filename = `${filename}.${extension}`;
}
totalSize += fileSize.size; totalSize += fileSize.size;
files.push({ files.push({
extension, extension,
filename, filename,
fullPath, fullPath,
mimeType: lookupMimeType(filename.toLowerCase()), mimeType: item.type || lookupMimeType(filename.toLowerCase()),
size: getFormattedFileSize(fileSize), size: getFormattedFileSize(fileSize),
type: item.type, type: item.type,
}); });