AWS Amplify Upload files | Best Practices - amazon-s3

If I'm not mistaken, to be able to upload a file to S3 you have to use a similar code:
await Storage.put(
e.target.files[0].name,
e.target.files[0],
{ contentType: e.target.files[0].type }
);
Is that way best and secure way? Are you sure?
Indeed, imagine I have the following scenario:
Scenario 1: Files must be uploaded in specific folders
Example:
const user_id = '30e29fc1-4718-4f35-9908-729d63477114';
await Storage.put(
user_id + '/' + e.target.files[0].name,
e.target.files[0],
{ contentType: e.target.files[0].type }
);
Anyone can override this condition by changing the path.
Example:
await Storage.put(
'other_path/' + e.target.files[0].name,
e.target.files[0],
{ contentType: e.target.files[0].type }
);
--> How to force users' files to be uploaded to their own folder?
Scenario 2: Upload the file then save the path in the DynamoDB database
Example:
const { key } = await Storage.put(
e.target.files[0].name,
e.target.files[0],
{ contentType: e.target.files[0].type }
);
const inputPost = {
id: uuid(),
title: 'My title',
content: 'My content',
filePath: key
};
await API.graphql(graphqlOperation(createPost, {input: inputPost}));
Anyone can override this condition by setting the filePath parameter.
Example:
const inputPost = {
id: uuid(),
title: 'My title',
content: 'My content',
filePath: 'perfume.jpg' // the file path of a file that does not belong to me
};
await API.graphql(graphqlOperation(createPost, {input: inputPost}));
--> How to prevent this kind of thing?
I show many many video tutorial with that practices. Are there ok?
Best Regard
Thank you
MEMO:
With Firebase, I have the same problem but I can set some Rules:
https://firebase.google.com/docs/storage/security
https://firebase.google.com/docs/reference/security/storage

Related

Error while uploading Multiple Files with different File Names With Skipper in Sails Js

I am trying to upload multiple type of files to S3 in sails js
Files are as Follow : VehicleImages, VehicleVideos, VehicleDocuments
I am passing these three type of files with attribute name with same name as these files to my server via multipart/form-data
My approach is to upload one type of file lets say VehicleImages by using await
Then upload the next and then the next.
I am unable to do so since i get the error :
{
"cause": {
"code": "EMAXBUFFER",
"message": "EMAXBUFFER: An upstream (`vehicleDocuments`) timed out before it was
plugged into a receiver. It was still unused after waiting 4500ms.
You can configure this timeout by changing the `maxTimeToBuffer`
option.\n\nNote that this error might be occurring due to an earlier
file upload that is finally timing out after an unrelated server
error."
},
"isOperational": true,
"code": "EMAXBUFFER"
}
This is my code for policies folder
VehicleController: {
create: ["isLoggedIn", "vehicleImages", "vehicleKey", "vehicleDocuments"],
update: ["isLoggedIn", "vehicleImages", "vehicleKey", "vehicleDocuments"],
"*": "isLoggedIn",
},
This is the helper function that i am using in my policies
fn: async function (inputs) {
const { req, res, proceed, fieldName, folderName, setParams } = inputs;
const skipperUpstream = req.file(fieldName);
const file = skipperUpstream._files[0];
if (!file) {
// `skipperUpstream.__proto__` is `Upstream`. It provides `noMoreFiles()` to stop receiving files.
// It also clears all timeouts: https://npmdoc.github.io/node-npmdoc-skipper/build/apidoc.html#apidoc.element.skipper.Upstream.prototype.noMoreFiles
skipperUpstream.noMoreFiles();
return proceed();
}
let timestamp = Date.now();
let uploadedFiles = await sails.upload(req.file(fieldName), {
adapter: require("skipper-s3"),
key: process.env.AWS_ACCESS_KEY_ID,
secret: process.env.AWS_SECRET_ACCESS_KEY,
bucket: process.env.AWS_BUCKET_NAME,
region: "us-east-2",
headers: {
"x-amz-acl": "public-read",
},
// maxBytes: sails.config.custom.maxFileSize,
// change folder and file name
dirname: folderName,
saveAs: function (__newFileStream, next) {
next(undefined, timestamp + __newFileStream.filename);
},
});
sails.log("filesUploaded In Helper", uploadedFiles);
if (uploadedFiles) {
uploadedFiles.forEach((file) => {
file.fd =
process.env.AWS_BUCKET_URL +
((folderName ? folderName + "%5C" : "") +
timestamp +
file.filename.replaceAll("\\", "%5C"));
});
sails.log("filesUploaded", uploadedFiles[0]);
req.files = uploadedFiles;
setParams && setParams(uploadedFiles);
}
return proceed();
},
My aim to upload large files (images, videos, documents) from react application to s3 bucket . How can I do this?

React Native - How to rename camera roll image file name

I'm having a problem using #react-native-community/cameraroll.
Here's my code:
const handleSaveImageToDevice = async () => {
try {
const uri = await captureRef(viewRef, {
format: 'jpg',
quality: 0.8,
});
const image = CameraRoll.save(uri, {
type: 'photo',
album: 'Generated Image',
});
} catch (e) {
return e;
}
};
As seen in the code I used captureRef to save react elements with ref as image.
The behavior is working as expected. If the user clicks the save button the image will be saved to the designated folder. But the problem is it saves random file name. I want to rename it like for example "generated_image".
When you are capturing there is option for image file name:
const uri = await captureRef(viewRef, {
format: 'jpg',
quality: 0.8,
fileName: 'generated_image'
});
Refer below link:
https://github.com/gre/react-native-view-shot/issues/116#issuecomment-1155523609
I forgot to update this but I already found a way to fix my problem using third party library react-native-fs
viewShot?.current?.capture().then((data) => {
RNFS.writeFile(
RNFS.CachesDirectoryPath +
`/generated-image.jpg`,
data,
'base64',
).then((success) => {
return CameraRoll.save(
RNFS.CachesDirectoryPath +
`/generated-image.jpg`,
{
type: 'photo',
album: 'testing',
},
);
});
});

Uploading large video files on background in react native

I need to upload large video files in the background.
What is the best way to achieve this?
I tried using this library (react-native-background-upload). But the problem is, I can't send any other data along with the video file. The API expects the following data:
{
projectId: number,
title: string,
video: file,
};
This is the piece of code to send the files with multipart using the library:
const options = {
url: url,
path: pathToVideoFile,
method: 'POST',
field: 'video',
type: 'multipart',
};
Are there any alternatives that I can use? Can react-native-background-fetch be used for this use case?
in react-native-background-upload
you can use parameters in options to send additional data
Additional form fields to include in the HTTP request. Only used when type: 'multipart
const options = {
url: url,
path: pathToVideoFile,
method: 'POST',
field: 'video',
type: 'multipart',
parameters : {
key1: "value1",
key2: "value2",
}
};
you can see all options params here here
you can install react-native-compressor package which is made by me
Installation
yarn add react-native-compressor
Usage
import {Video} from 'react-native-compressor';
const headers={
'Content-Type': '',
}
const uploadResult = await Video.backgroundUpload(
"http://w.hbu50.com:8080/hello.mp4",
fileUri,
{httpMethod: 'PUT', headers},
(written, total) => {
onProgress({
status: 'uploading',
progress: written / total,
uploading: true,
});
},
);

How to write a mutation resolver that handles image AND form data upload stream?

So, currently I’m able to successfully upload an image through the client to an S3 bucket. However, my question is, how would I go about including additional form fields, such as an accompanying title and/or body field, with the existing image file?
The server component primarily utilizes apollo-server-express, graphql-upload, and aws-sdk while the client just uses apollo-client
My end goal is to essentially be able to take the user’s form data on submit that consists of multiple text fields and an image file, and then upload the contents to the s3 bucket in its own individual directory.
Would it be possible to compile the non-image-file form data as a json, and then upload both the files (the json and the image file) as a batch?
Here are some snippets for some context:
// server/typedefs.js
const typeDefs = gql`
scalar Upload
type File {
id: ID!
filename: String!
mimetype: String!
encoding: String!
}
type Mutation {
singleUploadStream(file: Upload!): File!
}
type Query {
files: [File]
}
`;
// server/resolvers.js
...
...
Mutation: {
singleUploadStream: async (parent, args) => {
const file = await args.file;
const { createReadStream, filename } = file;
const fileStream = createReadStream();
const uploadParams = {
Bucket: process.env.BUCKET_NAME,
Key: `uploads/${filename}/${filename}`,
Body: fileStream,
};
await s3.upload(uploadParams).promise();
return file;
},
}
...
...
// server/index.js
...
...
const app = express();
const server = new ApolloServer({
typeDefs,
resolvers,
uploads: false,
introspection: true,
});
app.use(graphqlUploadExpress());
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () => {
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
});
please don’t hesitate to ask if you need more info or if you’re not sure what I’m asking for!
I really appreciate any help I can get ☹️ Thank you!
My resources:
https://www.apollographql.com/blog/graphql-file-uploads-with-react-hooks-typescript-amazon-s3-tutorial-ef39d21066a2/
https://dev.to/fhpriamo/painless-graphql-file-uploads-with-apollo-server-to-amazon-s3-and-local-filesystem-1bn0
https://www.thomasmaximini.com/upload-images-to-aws-s3-with-react-and-apollo-graphql

How to upload file to server using react-native

I am developing a app where i need to upload an image to the server. Based on the image i get a response which i need to render?.
Can you please help me how to upload an image using react-native?.
There is file uploading built into React Native.
Example from React Native code:
var photo = {
uri: uriFromCameraRoll,
type: 'image/jpeg',
name: 'photo.jpg',
};
var body = new FormData();
body.append('authToken', 'secret');
body.append('photo', photo);
body.append('title', 'A beautiful photo!');
var xhr = new XMLHttpRequest();
xhr.open('POST', serverURL);
xhr.send(body);
My solution is using fetch API and FormData.
Tested on Android.
const file = {
uri, // e.g. 'file:///path/to/file/image123.jpg'
name, // e.g. 'image123.jpg',
type // e.g. 'image/jpg'
}
const body = new FormData()
body.append('file', file)
fetch(url, {
method: 'POST',
body
})
I wrote something like that. Check out https://github.com/kamilkp/react-native-file-transfer
I have been struggling to upload images recently on react-native. I didn't seem to get the images uploaded. This is actually because i was using the react-native-debugger and network inspect on while sending the requests. Immediately i switch off network inspect, the request were successful and the files uploaded.
I am using the example from this answer above it works for me.
This article on github about the limitations of network inspect feature may clear things for you.
Just to build on the answer by Dev1, this is a good way to upload files from react native if you also want to show upload progress. It's pure JS, so this would actually work on any Javascript file.
(Note that in step #4 you have to replace the variables inside the strings with the type and file endings. That said, you could just take those fields out.)
Here's a gist I made on Github: https://gist.github.com/nandorojo/c641c176a053a9ab43462c6da1553a1b
1. for uploading one file:
// 1. initialize request
const xhr = new XMLHttpRequest();
// 2. open request
xhr.open('POST', uploadUrl);
// 3. set up callback for request
xhr.onload = () => {
const response = JSON.parse(xhr.response);
console.log(response);
// ... do something with the successful response
};
// 4. catch for request error
xhr.onerror = e => {
console.log(e, 'upload failed');
};
// 4. catch for request timeout
xhr.ontimeout = e => {
console.log(e, 'cloudinary timeout');
};
// 4. create formData to upload
const formData = new FormData();
formData.append('file', {
uri: 'some-file-path', // this is the path to your file. see Expo ImagePicker or React Native ImagePicker
type: `${type}/${fileEnding}`, // example: image/jpg
name: `upload.${fileEnding}` // example: upload.jpg
});
// 6. upload the request
xhr.send(formData);
// 7. track upload progress
if (xhr.upload) {
// track the upload progress
xhr.upload.onprogress = ({ total, loaded }) => {
const uploadProgress = (loaded / total);
console.log(uploadProgress);
};
}
2. uploading multiple files
Assuming you have an array of files you want to upload, you'd just change #4 from the code above to look like this:
// 4. create formData to upload
const arrayOfFilesToUpload = [
// ...
];
const formData = new FormData();
arrayOfFilesToUpload.forEach(file => {
formData.append('file', {
uri: file.uri, // this is the path to your file. see Expo ImagePicker or React Native ImagePicker
type: `${type}/${fileEnding}`, // example: image/jpg
name: `upload.${fileEnding}` // example: upload.jpg
});
})
In my opinion, the best way to send the file to the server is to use react-native-fs package, so install the package
with the following command
npm install react-native-fs
then create a file called file.service.js and modify it as follow:
import { uploadFiles } from "react-native-fs";
export async function sendFileToServer(files) {
return uploadFiles({
toUrl: `http://xxx/YOUR_URL`,
files: files,
method: "POST",
headers: { Accept: "application/json" },
begin: () => {
// console.log('File Uploading Started...')
},
progress: ({ totalBytesSent, totalBytesExpectedToSend }) => {
// console.log({ totalBytesSent, totalBytesExpectedToSend })
},
})
.promise.then(({ body }) => {
// Response Here...
// const data = JSON.parse(body); => You can access to body here....
})
.catch(_ => {
// console.log('Error')
})
}
NOTE: do not forget to change the URL.
NOTE: You can use this service to send any file to the server.
then call that service like the following:
var files = [{ name: "xx", filename:"xx", filepath: "xx", filetype: "xx" }];
await sendFileToServer(files)
NOTE: each object must have name,filename,filepath,filetype
A couple of potential alternatives are available. Firstly, you could use the XHR polyfill:
http://facebook.github.io/react-native/docs/network.html
Secondly, just ask the question: how would I upload a file in Obj-C? Answer that and then you could just implement a native module to call it from JavaScript.
There's some further discussion on all of this on this Github issue.
Tom's answer didn't work for me. So I implemented a native FilePickerModule which helps me choose the file and then use the remobile's react-native-file-transfer package to upload it. FilePickerModule returns the path of the selected file (FileURL) which is used by react-native-file-transfer to upload it.
Here's the code:
var FileTransfer = require('#remobile/react-native-file-transfer');
var FilePickerModule = NativeModules.FilePickerModule;
var that = this;
var fileTransfer = new FileTransfer();
FilePickerModule.chooseFile()
.then(function(fileURL){
var options = {};
options.fileKey = 'file';
options.fileName = fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType = 'text/plain';
var headers = {
'X-XSRF-TOKEN':that.state.token
};
options.headers = headers;
var url = "Set the URL here" ;
fileTransfer.upload(fileURL, encodeURI(url),(result)=>
{
console.log(result);
}, (error)=>{
console.log(error);
}, options);
})
Upload Files : using expo-image-picker npm module. Here we can upload any files or images etc. The files in a device can be accessed using the launchImageLibrary method. Then access the media on that device.
import * as ImagePicker from "expo-image-picker";
const loadFile = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
aspect: [4, 3],
});
return <Button title="Pick an image from camera roll" onPress={loadFile} />
}
The above code used to access the files on a device.
Also, use the camera to capture the image/video to upload by using
launchCameraAsync with mediaTypeOptions to videos or photos.