I am using amplify to upload and download files from my S3 bucket. There are two issues i am facing
The download link tries to download a random named json blob instead of a word document that i have in the bucket.(myword.docx comes out as random_characters.json). How can i get the actual S3 object as the filename to download.
When i execute the code, there are two download links for each file in the s3 bucket. I am unable to figure out what is wrong in the code to figure out the number of download links that are generated.
Your help is really appreciated.
import Amplify from "aws-amplify";
import "./App.css";
import { withAuthenticator } from "#aws-amplify/ui-react";
import "#aws-amplify/ui-react/styles.css";
import { Storage } from "aws-amplify";
import awsExports from "./aws-exports";
import { useEffect, useState } from "react";
Amplify.configure(awsExports);
function App({ signOut, user, sub}) {
const [fileData, setFileData] = useState();
const [fileStatus, setFileStatus] = useState(false);
const [s3DownloadLinks, setS3DownloadLinks] = useState([]);
const uploadFile = async () => {
const result = await Storage.put(fileData.name, fileData, {
level: "private",
contentType: fileData.type,
identityId: sub
});
setFileStatus(true);
console.log(21, result);
};
async function listObjectsFromS3() {
const s3Objects = await Storage.list('', {
level: "private",
//contentType: fileData.type,
identityId: sub
});
s3Objects.map(async (item) => {
console.log(30, item);
let downloadLink = await generateDownloadLinks(item.key);
console.log(30, downloadLink);
setS3DownloadLinks((s3DownloadLinks) => [
...s3DownloadLinks,
downloadLink,
]);
console.log(31, s3DownloadLinks);
});
}
async function generateDownloadLinks(fileKey) {
const result = await Storage.get(fileKey, {
level: 'private',
identityId: sub,
download: true
});
console.log(32, result);
return downloadBlob(result.Body, "filename");
}
async function downloadBlob(blob, filename) {
const a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = "output.docx";
return a;
}
useEffect(() => {
listObjectsFromS3();
}, []);
return (
<div className="App">
<h1>Hello {user.username}</h1>
<div>
<input type="file" onChange={(e) => setFileData(e.target.files[0])} />
</div>
<div>
<button onClick={uploadFile}>Upload file</button>
</div>
{fileStatus ? "File uploaded successfully" : ""}
<div id="demo" >
<h2> Your Transcribed outputs are available here</h2>
</div>
{/* List all s3 objects and download by clicking on the link */}
{s3DownloadLinks.map((item, index) => (
<div key={index}>
<a href={item} target="_blank" download="">
Link {index}
</a>
</div>
))}
<div><button onClick={signOut}>Sign out</button></div>
</div>
);
}
export default withAuthenticator(App);
Related
I am using Nextjs to build a full-stack application I working on the admin cms and I try to upload a file like an image or etc. I do this from this post with a little difference in UI.
The upload function is good and everything is good with no errors and no problems. But I need to show upload progress or a percentage to the user but every topic I read on every site doesn't help me. how to do this?
front-end code:
import Image from "next/image";
import { useRef, useState } from "react";
import Theme from "../components/layouts/theme";
const Home = (props) => {
const choosenFile = useRef();
const [staticImage, setStaticImage] = useState();
const handleForm = async (event) => {
event.preventDefault();
const myFile = choosenFile.current.files;
const fileUrl = URL.createObjectURL(myFile[0]);
const data = new FormData();
data.append("file", myFile[0]);
try {
const response = await console.log("status", response);
if (!response.ok) {
throw new Error("Something went wrong!");
}
const responseData = await response.json();
console.log("response", responseData);
} catch (err) {
console.log("upload", err);
}
};
const setImage = (param) => {
setStaticImage(URL.createObjectURL(param));
};
return (
<div className="container">
{staticImage && (
<>
<div
className="thumbnail"
style={{ width: "100px", height: "100px" }}
>
<Image
src={staticImage}
width={300}
height={300}
layout="responsive"
/>
</div>
<p>The upload percentage shows here: <span className="text-danger">10%</span></p>
</>
)}
<div className="row justify-content-center">
<div className="col-10 p-5">
<form onSubmit={handleForm}>
<div className="input-group mb-3">
<input
type="file"
name="docs"
ref={choosenFile}
onChange={(e) => {
setImage(e.target.files[0]);
}}
/>
</div>
<div>
<button type="submit" className="btn btn-sm btn-primary">
Send
</button>
</div>
</form>
</div>
</div>
</div>
);
};
Home.getLayout = function getLayout(Home) {
return <Theme>{Home}</Theme>;
};
export default Home;
The API route code:
import formidable from "formidable";
import fs from "fs";
export const config = {
api: {
bodyParser: false,
},
};
const post = async (req, res) => {
const form = new formidable.IncomingForm();
form.parse(req, async function (err, fields, files) {
await saveFile(files.file);
return res.status(201).json({ message: "Upload was succesfull!" });
});
};
const saveFile = async (file) => {
const data = fs.readFileSync(file.filepath);
try {
fs.writeFileSync(`./public/uploads/${file.originalFilename}`, data);
} catch (err) {
console.log('err', err);
}
await fs.unlinkSync(file.filepath);
return;
};
export default (req, res) => {
req.method === "POST"
? post(req, res)
: req.method === "PUT"
? console.log("PUT")
: req.method === "DELETE"
? console.log("DELETE")
: req.method === "GET"
? console.log("GET")
: res.status(404).send("");
};
I use the formidable package to handle formData and fs to manage file
To do this you can use axios. for more information visit npm.com/package/axios
in home.js file handle replace fetch request whit this code:
axios.post("/api/upload", data, {
onUploadProgress: (progressEvent) => {
// console.log('progressEvent', progressEvent)
if (progressEvent.bytes) {
console.log(Math.round((progressEvent.loaded / progressEvent.total)*100));
}
},
});
import React from "react";
import "./App.css";
function App() {
const showFile = () => {
if (window.File && window.FileReader && window.FileList && window.Blob) {
const preview = document.getElementById("show-text");
cosnt file = document.querySelector("input[type=file]").files[0];
const reader = new FileReader();
const logFile = /log.*/;
if (file.type.match(logFile)) {
reader.onload = function (event) {
preview.innerHTML = event.target.result;
};
} else {
preview.innerHTML = "<span class='error'>It doesn't seem to be a log file!</span>";
}
reader.readAsText(file);
}
};
return (
<div className="App">
<input type="file" onChange={showFile} />
<div id="show-text">Choose text File</div>
</div>
);
}
export default App;
I want to be able to read a log file from the input field and do extra querying. For example, after reading the log file, I would like to count how many '/about' routes have been called and how unique the call is in terms of which machine sent the GET request.
log file
Update
I understood this question wrong, but here is a solution: (You can do any file manipulation inside the parseFile() function)
Link to the CodeSandbox (https://codesandbox.io/s/logs-viewer-file-chooser-crq8g)
import { useState } from "react";
import { LazyLog } from "react-lazylog";
export default function App() {
const [logContent, setLogContent] = useState(null);
function renderLogs() {
return (
<LazyLog extraLines={1} enableSearch text={logContent} caseInsensitive />
);
}
const parseFile = async (fileContent) => {
// Do whatever you want to do with the content of the file
console.log(fileContent);
};
const showFile = async (e) => {
const file = e.target.files[0];
if (file) {
const data = await new Response(file).text();
await parseFile(data);
setLogContent(data);
}
};
return (
<div className="app">
<h1>Logs uploader</h1>
<input type="file" accept=".log" onChange={showFile} />
<hr className="divider" />
<div className="log-container">
{logContent ? renderLogs() : <h4>No logs</h4>}
</div>
</div>
);
}
You can try this library https://mozilla-frontend-infra.github.io/react-lazylog/, if you don't have access at build time, you need to fetch the .log file.
import { LazyLog } from "react-lazylog";
export default function App() {
const url = "https://online.com/file.log";
return (
<div style={{ height: 500, width: 902 }}>
<LazyLog extraLines={1} enableSearch url={url} caseInsensitive />
</div>
);
}
See the Sandbox https://codesandbox.io/s/logs-viewer-bsf9s
I was following one of the tutorials with React js when this isuue came up. I am using Cloudinary React SDK (React image and video upload). I am using their Upload Widget. But when I press the button to open the widget it gives me this error - 'TypeError: Cannot read property 'createUploadWidget' of undefined'
Here's the script src - <script src="https://widget.cloudinary.com/v2.0/global/all.js" type="text/javascript" ></script>
Here's the code of App.js
import React, { useState } from "react";
import "./App.css";
export default function App() {
const [imageUrl, setimageUrl] = useState(null);
const [imageAlt, setimageAlt] = useState(null);
const handleImageUpload = () => {
const { files } = document.querySelector('input[type="file"]');
const imageFile = document.querySelector('input[type="file"]');
// destructure the files array from the resulting object
const filesa = imageFile.files;
// log the result to the console
console.log("Image file", filesa[0]);
const formData = new FormData();
formData.append("file", files[0]);
// replace this with your upload preset name
formData.append("upload_preset", "xxxxxxx");
const options = {
method: "POST",
body: formData,
};
// replace cloudname with your Cloudinary cloud_name
return fetch(
"https://api.Cloudinary.com/v1_1/xxxxxx/image/upload",
options
)
.then((res) => res.json())
.then((res) => {
setimageUrl(res.secure_url);
setimageAlt(`An image of ${res.original_filename}`);
})
.catch((err) => console.log(err));
};
const openWidget = () => {
// create the widget
const widget = window.Cloudinary.createUploadWidget(
{
cloudName: "xxxxxx",
uploadPreset: "xxxxx",
},
(error, result) => {
if (result.event === "success") {
setimageUrl(result.info.secure_url);
setimageAlt(`An image of ${result.info.original_filename}`);
}
}
);
widget.open(); // open up the widget after creation
};
return (
<div className="app">
<section className="left-side">
<form>
<div className="form-group">
<input type="file" />
</div>
<button type="button" className="btn" onClick={handleImageUpload}>
Submit
</button>
<button type="button" className="btn widget-btn" onClick={openWidget}>
Upload Via Widget
</button>
</form>
</section>
<section className="right-side">
<p>The resulting image will be displayed here</p>
{imageUrl && (
<img src={imageUrl} alt={imageAlt} className="displayed-image" />
)}
</section>
</div>
);
}
Any help is greatly appreciated !
Cloudinary should be referenced in a lowercase -
...
const widget = window.cloudinary.createUploadWidget(
...
I'm trying to make a file uploader in ReactJS. I managed to get it all done, except for the part that I show the image to the user. I'm able to make the name show up, but the image does not appear.
I think it's going to be easier if I show the code, so, here it goes
FileUploader Component
import React, { useState } from 'react';
import axios from 'axios'
function FileUploader() {
const [file, setFile] = useState('')
const [fileName, setFileName] = useState('Choose File')
const [selectedFile, setSelectedFile] = useState({
filePath: '',
fileName: ''
})
const handleChange = e => {
setFile(e.target.files[0])
setFileName(e.target.files[0].name)
}
const handleUpload = async e => {
e.preventDefault()
const data = new FormData()
data.append('file', file)
const res = await axios.post('http://localhost:8000/upload', data)
const { path, originalname } = res.data
setSelectedFile({filePath: path, fileName: originalname})
}
return (
<>
<div className="custom-file mb-4">
<input type="file" className="custom-file-input" id="customFile" onChange={handleChange} />
<label className="custom-file-label" htmlFor="customFile">{fileName}</label>
</div>
<button onClick={handleUpload} className='btn btn-primary btn-block mt-4'>Upload</button>
{selectedFile.filePath !== '' ? (
<div className='row mt-5'>
<div className='col-md-6 m-auto'>
<h3 className='text-center'>{selectedFile.fileName}</h3>
<img style={{ width: '100%' }} src={selectedFile.filePath} alt='' />
</div>
</div>
) : null}
</>
);
}
export default FileUploader;
I think you should use blob image by using URL.createObjectUrl
Below is updated code
import React, { useState } from 'react';
import axios from 'axios'
function FileUploader() {
const [file, setFile] = useState('')
const [fileName, setFileName] = useState('Choose File')
const [selectedFile, setSelectedFile] = useState({
filePath: '',
fileName: ''
})
const [blobImage, setBlobImage] = useState() // <= add
const handleChange = e => {
setFile(e.target.files[0])
setFileName(e.target.files[0].name)
setBlobImage(URL.createObjectURL(e.target.files[0])) // <= add
}
const handleUpload = async e => {
e.preventDefault()
const data = new FormData()
data.append('file', file)
const res = await axios.post('http://localhost:8000/upload', data)
const { path, originalname } = res.data
setSelectedFile({filePath: path, fileName: originalname})
}
return (
<>
<div className="custom-file mb-4">
<input type="file" className="custom-file-input" id="customFile" onChange={handleChange} />
<label className="custom-file-label" htmlFor="customFile">{fileName}</label>
</div>
<button onClick={handleUpload} className='btn btn-primary btn-block mt-4'>Upload</button>
{selectedFile.filePath !== '' ? (
<div className='row mt-5'>
<div className='col-md-6 m-auto'>
<h3 className='text-center'>{selectedFile.fileName}</h3>
<img style={{ width: '100%' }} src={blobImage} alt='' /> // <= change src to blobImage
</div>
</div>
) : null}
</>
);
}
export default FileUploader;
I wrote an article about file uploading with React and DnD but it's not that far off from what you're trying to accomplish:
Build A React Drag & Drop Progress File Uploader
My guess is that you're wanting to display the file preview of the image that is about to be uploaded. You can do this by loading the image into the local state, although you'll have to be careful if it's a large image as it can crash the browser.
Click the "Run code snippet" to see it in action.
// main.js
const { useState } = React;
const App = () => {
// State / Props
const [preview, setPreview] = useState(null);
// Functions
const onInputFileChange = event => {
// reset each time
setPreview(null);
// Define supported mime types
const supportedFilesTypes = ['image/jpeg', 'image/png'];
if (event.target.files.length > 0) {
// Get the type of the first indexed file
const { type } = event.target.files[0];
if (supportedFilesTypes.indexOf(type) > -1) {
const reader = new FileReader();
reader.onload = e => { setPreview(e.target.result); };
reader.readAsDataURL(event.target.files[0]);
}
}
};
return (<div><h1>Choose an image</h1><input onChange={onInputFileChange} type="file" name="file" />{preview && <img src={preview} />}</div>);
};
ReactDOM.render(<App />, document.getElementById('root'));
<body>
<div id="root"></div>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<script type="text/babel" src="main.js"></script>
</body>
I am working on a simple drag and drop/input button file upload component. In the following component I get a file either through drag and drop or the input button:
React Component 1:
import React, {useState, useEffect} from 'react';
import ReactDOM from 'react-dom';
function ReceiptUploadPortal(props) {
const {element, uploadFile} = props;
const [errors, setErrors] = useState([]);
const [dropzoneActive, setDropzoneActive] = useState(false);
const [file, setFile] = useState();
const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'application/pdf'];
useEffect(() => {
const isValidSize = file && file.size > 6 * 1024;
const isValidType = file && allowedTypes.includes(file.type);
if (isValidSize && isValidType) {
setErrors([]);
uploadFile(file);
} else {
if (file && !isValidSize) setErrors([...errors, 'file exceeds maximum allowed size']);
if (file && !isValidType) setErrors([...errors, 'unsupported file type, please use a jpg file extension']);
}
}, [file])
function handleDragOver(event) {
event.preventDefault();
setDropzoneActive(true);
setErrors([]);
}
function handleDrop(event) {
event.preventDefault();
if (event.dataTransfer.files.length === 1) {
setFile(event.dataTransfer.files[0]);
} else {
setErrors(['too many files, please upload one image at a time'])
}
setDropzoneActive(false)
}
function handleFileSelect(event) {
setErrors([]);
setFile(event.target.files[0]);
}
return ReactDOM.createPortal(
<div className="receipt-upload-widget-container">
<div className="file-info-wrapper">
<div className="file-info">
<span>FILE TYPE</span><br />
<span>JPG</span>
</div>
<div className="file-info">
<span>MAX SIZE</span><br />
<span>6 MB</span>
</div>
</div>
<div
className={`file-upload-drop-zone ${dropzoneActive ? 'active' : ''}`}
onDrop={(event) => handleDrop(event)}
onDragOver={(event) => handleDragOver(event)}>
<p>
Drag & Drop<br />
or upload your image manually
</p>
</div>
<div className="file-input-wrapper">
<label>
Upload
<input
type="file"
accept="image/*"
onChange={(event) => handleFileSelect(event)} />
</label>
<p>{file && file.name}</p>
</div>
<div className="fileErrors">
{errors.map((error, index) => <div className="fileError" key={index}>{error}</div>)}
</div>
</div>, element
);
}
export default ReceiptUploadPortal;
React Component 2:
import React, {useState} from 'react';
import ReceiptUploadPortal from './ReceiptUploadPortal';
import {receiptUpload} from '../../api/FileUploadService';
import NotificationComponent from '../../ui-components/NotificationComponent';
import { successNotification, errorNotification } from '../../helpers/notficationHelper';
function ReceiptUploadContainer() {
const receiptUploadWidgets = [...document.querySelectorAll(`div[data-widget='ReceiptUpload']`)];
const [notification, setNotfication] = useState({
...successNotification,
onClosed: () => setNotfication({...notification, show: false})
});
async function handleUpload(file) {
const req = await receiptUpload(file);
if (req) {
setNotfication({
...notification,
show: true
})
} else {
setNotfication({
...notification,
...errorNotification,
show: true
});
}
}
return (
<div id="ReceiptUploadContainer">
{receiptUploadWidgets.map((element, index) =>
<ReceiptUploadPortal
key={index}
element={element}
uploadFile={(file) => handleUpload(file)} />
)}
<NotificationComponent {...notification}/>
</div>
);
}
export default ReceiptUploadContainer
Then this triggers a service to be called which is where things are going wrong...
service:
import axios from 'axios';
import { urls } from './Urls';
import { getAccountInfo } from './GigyaService';
import { getAWSHeaders } from '../helpers/getAWSHeaders';
export async function receiptUpload(file) {
const account = await getAccountInfo();
const headers = getAWSHeaders(account);
const presignUrl = await axios.post(urls.fileUpload.presignUpload, {fileName: file.name}, {headers}).then(res => res.data);
const fileUpload = presignUrl && await axios.put(presignUrl.presignUrl, file, {'Content-Type': file.type}).then(res => res.status);
const fileId = fileUpload && await axios.post(urls.fileUpload.receiptUpload, {file_name: file.name}, {headers}).then(res => res.data);
const receiptSubmit = fileId && await axios.post(urls.fileUpload.receiptSubmit, {
id_type: 'third_party_id',
file_id: fileId.file_id
}, {headers}).then(res => res.data);
return receiptSubmit;
}
Everything works up to the point where I try to post the actual file to S3.
Meaning, I am able to get the pre-signed url. However, when I upload the file I get a 200 response with no data payload, and I can see in S3 that the file is not there.
This of course causes the next two requests to fail.
Any thoughts on what I may be doing wrong here?