I am using Nodejs server and Android for image uploading.
Node js code
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, '../uploads');
},
filename: (req, file, cb) => {
console.log('file ',file);
cb(null, (Date.now() + path.extname(file.originalname)));
}
});
const fileFilter = (req, file, cb) => {
if (file.mimetype == 'image/jpeg' || file.mimetype == 'image/png') {
cb(null, true);
} else {
cb(null, false);
}
}
const upload = multer({ storage: storage, fileFilter: fileFilter });
app.post('/upload', upload.single('file'), (req, res, next) => {
const file = req.file // return undefined
if (!file) {
const error = new Error('Please upload a file')
error.httpStatusCode = 400
return next(error)
}
res.send(file)
})
Android code
File file = new File(compressedImagePath);
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), RequestBody.create(MediaType.parse("image/*"), file));
Call<ImageUploadResponse> call = service.uploadImage(filePart);
This returns in undefined in the server in the following code line.
const file = req.file
Is there any issue on both sides? This works perfectly in the postman.
Related
I’m downloading a file, and I’ve expected open the file after finished downloading, but this does not work, it’s viewed on a black screen when opening using Linking, and I open this file in the folder where it is and it opens correctly. How can I fix this?
This is the URL of the file in STORAGE:
content://com.android.externalstorage.documents/tree/primary%3ADownload%2FSigaa/document/primary%3ADownload%2FSigaa%2Fhistory.pdf
My code:
const downloadPath = FileSystem.documentDirectory! + (Platform.OS == "android" ? "" : "");
const ensureDirAsync: any = async (dir: any, intermediates = true) => {
const props = await FileSystem.getInfoAsync(dir);
if (props.exists && props.isDirectory) {
return props;
}
let _ = await FileSystem.makeDirectoryAsync(dir, { intermediates });
return await ensureDirAsync(dir, intermediates);
};
const downloadFile = async (fileUrl) => {
if (Platform.OS == "android") {
const dir = ensureDirAsync(downloadPath);
}
let fileName = "history";
const downloadResumable = FileSystem.createDownloadResumable(
fileUrl,
downloadPath + fileName,
{}
);
console.log(downloadPath);
try {
const { uri } = await downloadResumable.downloadAsync();
saveAndroidFile(uri, fileName);
} catch (e) {
console.error("download error:", e);
}
};
const saveAndroidFile = async (fileUri, fileName = "File") => {
try {
const fileString = await FileSystem.readAsStringAsync(fileUri, { encoding: FileSystem.EncodingType.Base64 });
if (local === "") {
const permissions = await StorageAccessFramework.requestDirectoryPermissionsAsync();
if (!permissions.granted) {
return;
}
await AsyncStorage.setItem("local", permissions.directoryUri);
}
try {
await StorageAccessFramework.createFileAsync(local, fileName, "application/pdf")
.then(async (uri) => {
await FileSystem.writeAsStringAsync(uri, fileString, { encoding: FileSystem.EncodingType.Base64 });
Linking.openURL(uri);
alert("Success!");
})
.catch((e) => {});
} catch (e) {
throw new Error(e);
}
} catch (err) {}
};
The black screen when opening:
I solved this by implementing the intent launcher api for android and the sharing api for ios.
To make it work it is important to provide the mimeType of the file (or UTI for IOS). You can extract it manually from the file extension but there's a library for that on RN: react-native-mime-types.
Actually I think any library that doesn't deppend on node core apis shoud work just fine.
Here's the code:
const openFile = async (fileUri: string, type: string) => {
try {
if (Platform.OS === 'android') {
await startActivityAsync('android.intent.action.VIEW', {
data: fileUri,
flags: 1,
type,
});
} else {
await shareAsync(fileUri, {
UTI: type,
mimeType: type,
});
}
} catch (error) {
// do something with error
}
};
I am getting uri from react-native-image-picker and I am storing the image data in an array then sending the images Array in in network call.
When I'm taking all images and save uri in state without cancelling the react-native-image-picker CameraActivity and sending all images through network call, that works perfectly.
But when I take one or more image and save uri in state, later if I open camera and go back from the react-native-image-picker cameraActivity without taking any picture, it gives me this error during network call:
Could not retrieve file for contentUri file:///storage/emulated/0/Pictures/images/image-e4da20e6-25a3-4e0f-a84c-d42ad9be7226.jpg
java.io.FileNotFoundException: /storage/emulated/0/Pictures/images/image-e4da20e6-25a3-4e0f-a84c-d42ad9be7226.jpg: open failed: ENOENT (No such file or directory)
at libcore.io.IoBridge.open(IoBridge.java:496)
at java.io.FileInputStream.<init>(FileInputStream.java:159)
This behaviour is really confusing to me.
I am using this function to take image and store it in state:
RNCamera()
.then(({ uri }) => {
setImages((prev) => [...prev, { uri: uri }]);
})
And I am sending request with the image object as FormData:
export const uploadFile = async (file: any, authToken?: any) => {
try{
const formData = new FormData();
formData.append('file', file);
let token = authToken;
if(!authToken) token = await AsyncStorage.getItem('userToken');
const res = await customFetch(FILE_UPLOAD_URL, {
method: "POST",
body: formData,
headers: {
'authorization': token ? `Bearer ${token}` : '',
}
});
if(!res.ok) throw new Error(res.statusText || `Error: Status is ${res.status}`)
return await res.json();
}catch(error: any){
throw new Error(error.message || "Error uploading file")
}
};
I am sending the file to uploadFile like this:
const fileIds = await Promise.all(images?.map(async img => {
const randomName = dateString + '_photo_' + Math.random() + ".jpg";
const file = {
uri: img.uri,
name: randomName,
type: 'image/jpg'
};
const res = await uploadFile(file, storeState?.userToken);
return res?.id
}))
I'm using Firestore v9 within Expo/React Native. I use XMLHttpRequest to convert a base64 image into a blob file. Then I send/upload that blob file to Firebase Storage. It works perfectly with iOS but not with Android. In Android it returns {istrusted: false}, like the network request is not sent or something went wrong during converting the base64 image into a blob file.
I tried to use react-native-fetch-blob library as mentioned here but it's not supported by Expo.
This is my code:
const getBlob = async (photo) => {
return await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log("consoleloge",e);
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", photo, true);
xhr.send(null);
});
}
const upload64BaseImage = async (photo, newDocRef) => {
try {
const blob = await getBlob(photo)
const imageRef = ref(storage, `images/${newDocRef}.jpg`);
const metadata = {
contentType: "image/jpeg",
};
const snapshot = await uploadBytes(imageRef, blob, metadata);
const downloadUrl = await getDownloadURL(snapshot.ref);
return downloadUrl;
} catch (error) {
console.log(`error`, error.message);
}
};
I have a react-native app on Android and a backend server written in NodeJS + Express and I'm using multer to handle file uploads.
const multer = require('multer');
const mime = require('mime');
const crypto = require('crypto');
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, config.uploads),
filename: (req, file, cb) => {
crypto.pseudoRandomBytes(16, (err, raw) => {
cb(null, raw.toString('hex') + Date.now() + '.' + mime.extension(file.mimetype));
});
}
});
const upload = multer({ storage });
const Router = require('express').Router;
const controller = require('./upload.controller');
const router = new Router();
const auth = require('./../../auth/auth.service');
router.post('/', [auth.isAuthenticated(), upload.any()], controller.create);
module.exports = router;
And on my react-native app I try to do like this:
ImagePicker.launchCamera(options, image => {
let { uri } = image
const API_URL = 'http://192.168.1.2:9000/api/uploads'
var form = new FormData();
form.append("FormData", true)
form.append("access_token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjU3YjgyZGQ2MTEwZDcwYmEwYjUxZjM5YyIsImlzTWVkaWMiOnRydWUsImlhdCI6MTQ3MTY4ODE1MiwiZXhwIjoxNDcxNzA2MTUyfQ.gPeql5g66Am4Txl1WqnbvOWJaD8srTK_6vihOJ6kFbY")
form.append("Content-Type", "image/jpg")
form.append('image', uri)
fetch(API_URL, {body: form, mode: "FormData", method: "post", headers: {"Content-Type": "multipart/form-data"}})
.then((response) => console.log(response))
.catch((error) => {
console.log("ERROR " + error)
})
.then((responseData) => {
console.log("Succes "+ responseData)
})
.done();
})
But when I try to upload I recive the following error
multipart body must have at least one part
I am doing something wrong?
Does anybody knows a better solution to do this?
Fetch may not support Blob and FormData at this moment, but you can use XMLHttpRequest polyfill instead.
let xhr = new XMLHttpRequest()
xhr.open('post', `http://myserver.com/upload-form`)
xhr.send(form)
xhr.onerror = function(e) {
console.log('err', e)
}
xhr.onreadystatechange = function() {
if(this.readyState === this.DONE) {
console.log(this.response)
}
}
I tried to insert an image to db and get back as json data for mobile developing. I can successfully insert the path and retrieve the path as json but when i my friend tried to access the url it shows no such file or found.
This is the code
var express=require("express");
var app=express();
var fs = require("fs");
var sql=require('mysql');
var http = require("http");
var server = http.createServer();
var bodyParser = require('body-parser');
// app.use(bodyParser());
var multer = require('multer');
// var upload=multer({dest:'tmp/'});
// app.use(express.static('public'));
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'C:/Users/Ramachandran/Desktop/File/tmp/upload')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage })
app.use(bodyParser.urlencoded({extended: true}));
var con=sql.createConnection({
host:"localhost",
user:"root",
password:'',
database:'test'
});
app.get('/',function (req,res){
con.connect(function(err){
if(err){
console.log(err);
return;
}
console.log("connection established");
res.send("connection established");
});
})
app.get('/index',function(req,res){
res.sendFile('index.html',{'root': __dirname });
})
app.post('/insert', upload.single("myfile"), function (req,res){
var tes = __dirname + "/" + req.file.originalname;
fs.writeFile(tes, data, function (err) {
if (err) {
console.log(err);
} else {
var response = {
message: 'File uploaded successfully',
filename: req.file.originalname
};
}
console.log(response);
});
var data = {
uid:req.body.RollNo,
pat:req.file.path
};
con.query("insert into src set ?",data, function (err,rows){
if(err) throw err;
res.send("Value has bee inserted");
})
})
app.get('/test',function(req,res){
con.query('select * from src',function (err,rows){
if(err) throw err;
console.log("data receive from db");
console.log(rows);
res.send(rows);
})
})
app.listen(8888);
This my Json data
[{"uid":78965,"pat":"C:\\Users\\Vasanth\\Desktop\\File\\tmp\\upload\\myfile-1467012273947"},{"uid":987,"pat":"C:\\Users\\Vasanth\\Desktop\\File\\tmp\\upload\\myfile-1467012387236"}]
This will not work as you are returning a local path. The file is present on your machine, but when your friend tries to download it, he is trying to find it in C:\\Users\\Vasanth\\Desktop\\File\\tmp\\upload\\myfile-1467012273947 on his machine. You need to save the file on a folder, that is exposed by the server and provide the file from there, i.e. - http://localhost:8888/files/myfile-1467012273947 The easiest way to achieve this is using the express.static middleware: http://expressjs.com/en/starter/static-files.html