[FIX] Filenames are incorrect in non-latin alphabets on upload (#2671)
* fix: filename on react-native-image-crop-picker * fix: use rn-fetch-blob to upload files * fix: FileUpload as a service * fix: cancel upload on iOS * fix: file upload from share extension Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
b0b9d62a91
commit
2403eb3857
|
@ -1,6 +1,7 @@
|
||||||
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||||
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
||||||
|
|
||||||
|
import FileUpload from '../../utils/fileUpload';
|
||||||
import database from '../database';
|
import database from '../database';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
|
|
||||||
|
@ -12,7 +13,11 @@ export function isUploadActive(path) {
|
||||||
|
|
||||||
export async function cancelUpload(item) {
|
export async function cancelUpload(item) {
|
||||||
if (uploadQueue[item.path]) {
|
if (uploadQueue[item.path]) {
|
||||||
uploadQueue[item.path].abort();
|
try {
|
||||||
|
await uploadQueue[item.path].cancel();
|
||||||
|
} catch {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
await db.action(async() => {
|
await db.action(async() => {
|
||||||
|
@ -32,9 +37,6 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) {
|
||||||
|
|
||||||
const uploadUrl = `${ server }/api/v1/rooms.upload/${ rid }`;
|
const uploadUrl = `${ server }/api/v1/rooms.upload/${ rid }`;
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
const formData = new FormData();
|
|
||||||
|
|
||||||
fileInfo.rid = rid;
|
fileInfo.rid = rid;
|
||||||
|
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
|
@ -56,31 +58,38 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadQueue[fileInfo.path] = xhr;
|
const formData = [];
|
||||||
xhr.open('POST', uploadUrl);
|
formData.push({
|
||||||
|
name: 'file',
|
||||||
formData.append('file', {
|
|
||||||
uri: fileInfo.path,
|
|
||||||
type: fileInfo.type,
|
type: fileInfo.type,
|
||||||
name: encodeURI(fileInfo.name) || 'fileMessage'
|
filename: fileInfo.name || 'fileMessage',
|
||||||
|
uri: fileInfo.path
|
||||||
});
|
});
|
||||||
|
|
||||||
if (fileInfo.description) {
|
if (fileInfo.description) {
|
||||||
formData.append('description', fileInfo.description);
|
formData.push({
|
||||||
|
name: 'description',
|
||||||
|
data: fileInfo.description
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmid) {
|
if (tmid) {
|
||||||
formData.append('tmid', tmid);
|
formData.push({
|
||||||
|
name: 'tmid',
|
||||||
|
data: tmid
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
xhr.setRequestHeader('X-Auth-Token', token);
|
const headers = {
|
||||||
xhr.setRequestHeader('X-User-Id', id);
|
...RocketChatSettings.customHeaders,
|
||||||
const { customHeaders } = RocketChatSettings;
|
'Content-Type': 'multipart/form-data',
|
||||||
Object.keys(customHeaders).forEach((key) => {
|
'X-Auth-Token': token,
|
||||||
xhr.setRequestHeader(key, customHeaders[key]);
|
'X-User-Id': id
|
||||||
});
|
};
|
||||||
|
|
||||||
xhr.upload.onprogress = async({ total, loaded }) => {
|
uploadQueue[fileInfo.path] = FileUpload.fetch('POST', uploadUrl, headers, formData);
|
||||||
|
|
||||||
|
uploadQueue[fileInfo.path].uploadProgress(async(loaded, total) => {
|
||||||
try {
|
try {
|
||||||
await db.action(async() => {
|
await db.action(async() => {
|
||||||
await uploadRecord.update((u) => {
|
await uploadRecord.update((u) => {
|
||||||
|
@ -90,15 +99,14 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
xhr.onload = async() => {
|
uploadQueue[fileInfo.path].then(async(response) => {
|
||||||
if (xhr.status >= 200 && xhr.status < 400) { // If response is all good...
|
if (response.respInfo.status >= 200 && response.respInfo.status < 400) { // If response is all good...
|
||||||
try {
|
try {
|
||||||
await db.action(async() => {
|
await db.action(async() => {
|
||||||
await uploadRecord.destroyPermanently();
|
await uploadRecord.destroyPermanently();
|
||||||
});
|
});
|
||||||
const response = JSON.parse(xhr.response);
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
|
@ -114,15 +122,14 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = JSON.parse(xhr.response);
|
|
||||||
reject(response);
|
reject(response);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
xhr.onerror = async(error) => {
|
uploadQueue[fileInfo.path].catch(async(error) => {
|
||||||
try {
|
try {
|
||||||
await db.action(async() => {
|
await db.action(async() => {
|
||||||
await uploadRecord.update((u) => {
|
await uploadRecord.update((u) => {
|
||||||
|
@ -133,9 +140,7 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
reject(error);
|
reject(error);
|
||||||
};
|
});
|
||||||
|
|
||||||
xhr.send(formData);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import RNFetchBlob from 'rn-fetch-blob';
|
||||||
|
|
||||||
|
class FileUpload {
|
||||||
|
fetch = (method, url, headers, data) => {
|
||||||
|
const formData = data.map((item) => {
|
||||||
|
if (item.uri) {
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
type: item.type,
|
||||||
|
filename: item.filename,
|
||||||
|
data: RNFetchBlob.wrap(decodeURI(item.uri))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
return RNFetchBlob.fetch(method, url, headers, formData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileUpload = new FileUpload();
|
||||||
|
export default fileUpload;
|
|
@ -0,0 +1,48 @@
|
||||||
|
class FileUpload {
|
||||||
|
_xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
_formData = new FormData();
|
||||||
|
|
||||||
|
fetch = (method, url, headers, data) => {
|
||||||
|
this._xhr.open(method, url);
|
||||||
|
|
||||||
|
Object.keys(headers).forEach((key) => {
|
||||||
|
this._xhr.setRequestHeader(key, headers[key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
if (item.uri) {
|
||||||
|
this._formData.append(item.name, {
|
||||||
|
uri: item.uri,
|
||||||
|
type: item.type,
|
||||||
|
name: item.filename
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._formData.append(item.name, item.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
then = (callback) => {
|
||||||
|
this._xhr.onload = () => callback({ respInfo: this._xhr });
|
||||||
|
this._xhr.send(this._formData);
|
||||||
|
}
|
||||||
|
|
||||||
|
catch = (callback) => {
|
||||||
|
this._xhr.onerror = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadProgress = (callback) => {
|
||||||
|
this._xhr.upload.onprogress = ({ total, loaded }) => callback(loaded, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel = () => {
|
||||||
|
this._xhr.abort();
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileUpload = new FileUpload();
|
||||||
|
export default fileUpload;
|
|
@ -74,7 +74,7 @@ class ShareListView extends React.Component {
|
||||||
}
|
}
|
||||||
const info = await Promise.all(data.filter(item => item.type === 'media').map(file => FileSystem.getInfoAsync(this.uriToPath(file.value), { size: true })));
|
const info = await Promise.all(data.filter(item => item.type === 'media').map(file => FileSystem.getInfoAsync(this.uriToPath(file.value), { size: true })));
|
||||||
const attachments = info.map(file => ({
|
const attachments = info.map(file => ({
|
||||||
filename: file.uri.substring(file.uri.lastIndexOf('/') + 1),
|
filename: decodeURIComponent(file.uri.substring(file.uri.lastIndexOf('/') + 1)),
|
||||||
description: '',
|
description: '',
|
||||||
size: file.size,
|
size: file.size,
|
||||||
mime: mime.lookup(file.uri),
|
mime: mime.lookup(file.uri),
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
diff --git a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
|
||||||
|
index 3500542..94e45b6 100644
|
||||||
|
--- a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
|
||||||
|
+++ b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
|
||||||
|
@@ -584,6 +584,7 @@ class PickerModule extends ReactContextBaseJavaModule implements ActivityEventLi
|
||||||
|
image.putInt("height", options.outHeight);
|
||||||
|
image.putString("mime", options.outMimeType);
|
||||||
|
image.putInt("size", (int) new File(compressedImagePath).length());
|
||||||
|
+ image.putString("filename", compressedImage.getName());
|
||||||
|
image.putString("modificationDate", String.valueOf(modificationDate));
|
||||||
|
|
||||||
|
if (includeBase64) {
|
Loading…
Reference in New Issue