I have excel file coming from API and I intend to implement a download button for users to click and download the file.
import FileDownload from "js-file-download";
function Download() {
const [facilityName, setFacilityName] = useState("");
const handleClick = (e) => {
e.preventDefault()
Axios({
url: `apiEndpoint/${facilityName}`,
method: "POST",
responseType:"blob",
}).then((res)=>{
console.log("myres", res)
FileDownload(res.data, `${facilityName}.xlsx`)
})
}
return (
<div>
<TextField
className="box"
name="facility_name"
select
required
value={facilityName}
onChange={(e) => setFacilityName(e.target.value)}
/>
<Button onClick={(e) => handleClick(e)}>
Download File
</Button>
</div>
);
}
export default Download;
When I click the download button, the file downloaded as excel but when I open the file, it reads "There was a problem with the content in filename.xlsx and so on". I have to download other excel file by change the {facility} var on the API and still got same error msg, only a blank page.
please i'm confused at this point, what am I missing in order to get the content of the file properly.
Related
I'm currently building some functionality in my React web app to upload a picture, display it on the client (this works), but also store the photo details so they can be sent in an axios post to my backend where the image gets stored in the file system.
Here's my code to now:
const SetupDetails = () => {
const [previewPhoto, setPreviewPhoto] = useState(null)
const [photoFile, setPhotoFile] = useState(null)
const uploadPhoto = (e) => {
e.preventDefault()
setPreviewPhoto(URL.createObjectURL(e.target.files[0]))
setPhotoFile(e.target.files[0])
}
const buttonClick = (e) => {
e.preventDefault()
console.log(previewPhoto)
console.log(photoFile)
axios.post("/api/uploadProfilePicture", {
photoFile: photoFile
})
}
const deletePhoto = (e) => {
e.preventDefault()
setPreviewPhoto(null)
setPhotoFile(null)
}
return (
<label for="photo-upload" className="setup-photo-label">Upload</label>
<input className="setup-photo-input" id="photo-upload" type="file" onChange={uploadPhoto} />
{
previewPhoto ?
<div className="preview-photo-container">
<img className="preview-photo" src={previewPhoto} alt="A small thumbnail uploaded by the user." />
<button className="preview-photo-delete" onClick={deletePhoto}>Delete</button>
</div> : null
}
<button className="setup-continue-button" onClick={buttonClick}>Continue</button>
)
}
The basic premise is:
The input calls a function during 'onChange' which updates state to store the details of the photo
The 'photoFile' details are posted to the backend to be stored in the backend
The 'previewPhoto' is used to display a thumbnail of the image to the user
However, the photoFile is returned as undefined, or an empty object, every time.
I'm not 100% sure where I'm going wrong, as the console.log of the 'photoFile' shows the details of the photo, so I'd have thought I could just post that to the backend.
Any advice here would be really appreciated!
I am using the React Dropzone library for file uploads. I have noticed that the files clear everytime the file box is clicked. For example, if you add one file, then click the box again to add another file, the original disappears. I have tried using the onDrop function in dropzone but I havent been able to figure that our. I am new to React so I would love some pointers on where to find information on this.
react dropzone has an argument name multiple which you can specify if you want to let user select/drag multiple files. the default value for this argument is true so this is how i use the library:
const { getRootProps, getInputProps } = useDropzone({
accept: '.jpeg,.png,.jpg',
onDrop: acceptedFiles => {
if (acceptedFiles.length === 0) {
return;
}
const newFiles = acceptedFiles.map(file => {
return {
file,
preview: URL.createObjectURL(file),
};
});
let newFilesState = [...files.concat(newFiles)];
//here i add the previously added files to new state and concat them with newly droped files
},
});
here is my jsx
<Button onClick={() => {
inputEl.current.click();
}}
>
<div {...getRootProps()}>
<input
// force input to re-render on file change
{...getInputProps()}
ref={inputEl}
/>
</div>
</Button>
note that variable named files is my redux which stores the previously added files. and after i receive a dropped/selected new file(S) is concat them with my files stored in redux
Below I have the index page of a Next.JS app configured with an AWS S3 backend:
import styles from "../styles/Home.module.css";
import { useState, useEffect } from "react";
import { Storage } from "aws-amplify";
export default function Home() {
const [songs, setSongs] = useState(null);
useEffect(() => {
fetchSongs();
}, []);
async function fetchSongs() {
let songKeys = await Storage.list("");
console.log("before signing", songKeys);
songKeys = await Promise.all(
songKeys.map(async (k) => {
const signedUrl = await Storage.get(k.key);
return signedUrl;
})
);
console.log("after signing", songKeys);
setSongs(songKeys);
}
async function onChange(event) {
const file = event.target.files[0];
const result = await Storage.put(file.name, file, {
contentType: "audio/*",
});
console.log(result);
fetchSongs();
}
return (
<div className={styles.container}>
<main className={styles.main}>
<h1 className={styles.title}>Music Streaming Site</h1>
<input type="file" onChange={onChange} accept="audio/*" />
{songs &&
songs.map((song) => (
<div key={song}>
<audio controls>
<source src={song} type="audio/mpeg" />
</audio>
</div>
))}
</main>
</div>
);
}
Currently, when I upload two files of the same name, AWS takes the first file, and the UI updates. But when the second file of the same name is uploaded, AWS takes it, and replaces the original file with it, or in other words, modify the file since the Last Modified column of my AWS console gets a new timestamp at the time the second file was uploaded.
I would like unique keys to be generated each time I upload a file. I would also like to be able to get the file by these unique keys individually, and present them via the UI.
If there's any way for AWS to prevent two of the same file with different names from being uploaded, that would be cool too.
I used mongodb , gridfs ,multer to store the audio file.
And need to show the audio file in reactjs frontend through axios.
app.get('/songs/getsong/:filename', (req, res) => {
console.log(req.params.filename);
const file = gfs
.find({
filename: req.params.filename
})
.toArray((err, files) => {
if (!files || files.length === 0) {
return res.status(404).json({
err: "no files exist"
});
}
gfs.openDownloadStreamByName(req.params.filename).pipe(res);
console.log(files)
return res.status(200);
});
});
Display audio file in frontend
import React, { useEffect ,useState} from 'react'
import Header from '../header/Header';
import "./home.css"
import axios from 'axios';
export default function Home(){
const [audiofile , setAudioFile] = useState()
axios.get('http://localhost:8000/songs/getsong/379500b35576a4c49792718aebfc27ef.mp3')
.then((res) => {
console.log(res);
setAudioFile(res)
}).catch((error) => {
console.log(error)
});
return (
<div>
<Header />
<form action="http://localhost:8000/songs/upload" method="post" enctype="multipart/form-data">
<h1>File Upload</h1>
<input id="file" type="file" accept="audio/*" name='file' />
<input type="submit" />
</form>
<audio
controls
src={audiofile}>
Your browser does not support the
<code>audio</code> element.
</audio>
</div>
)
}
this is the file request
{
_id: 60774f8560de1713d4b06234,
length: 11756413,
chunkSize: 261120,
uploadDate: 2021-04-14T20:25:25.300Z,
filename: '379500b35576a4c49792718aebfc27ef.mp3',
md5: '36d8d712eb886859b07952e49d4de6a6',
contentType: 'audio/mpeg'
}
In postman it showed a audio file and it works fine. I need to show it
on frontend.
Check Postman response ss here
Thank you.
Locally, you can use the HTML5 Audio tag to play the audio, feed src with URL of the audio and , that's it.
its probably not playing because you are setting audio file with res instead of res.data, then confirm the format of the response. its usually in binary format so you need to convert before playing, you can blob() and URL.createObjectURL()
Background
When running my app over localhost, I can choose my PDF file and submit it. I'm able to get the path of the IPFS file and display the path in the console.
Problem
When adding this line to display my file, it doesn't work and shows "No PDF file specified" instead.
<Document src={https://ipfs.infura.io/ipfs/${this.state.ipfshash}} />
<Document file={https://ipfs.infura.io/ipfs/${this.state.docupayHash}} />
What I've Tried
I've gone to the link in Google Chrome (ipfs.infura.io/ipfs/"QmUqB9dWDCeZ5nth9YKRJTQ6PcnfrGPPx1vzdyNWV6rh8s") and I can see the file there, so I know the link is correct.
Code
App.js
import React, { Component } from "react";
import { Document, Page } from 'react-pdf';
import web3 from "./web3";
import ipfs from "./ipfs";
import storehash from "./storehash";
import "./styles/App.css";
class App extends Component {
state = {
contractHash: null,
buffer: "",
ethAddress: "",
blockNumber: "",
transactionHash: ""
};
captureFile = (event) => {
event.stopPropagation()
event.preventDefault();
const file = event.target.files[0];
let reader = new window.FileReader();
reader.readAsArrayBuffer(file);
reader.onloadend = () => this.convertToBuffer(reader);
};
convertToBuffer = async (reader) => {
// Convert file to buffer so that it can be uploaded to IPFS
const buffer = await Buffer.from(reader.result);
this.setState({buffer});
};
onClick = async () => {
try {
await web3.eth.getTransactionReceipt(this.state.transactionHash, (err, txReceipt) => {
console.log(err, txReceipt);
this.setState({txReceipt});
});
} catch (error) {
console.log(error);
}
}
onSubmit = async (event) => {
event.preventDefault();
// Take the user's MetaMask address
const accounts = await web3.eth.getAccounts();
console.log("Sending from Metamask account: " + accounts[0]);
// Retrieve the contract address from storehash.js
const ethAddress= await storehash.options.address;
this.setState({ethAddress});
// Save document to IPFS, return its hash, and set it to state
await ipfs.add(this.state.buffer, (err, contractHash) => {
console.log(err, contractHash);
this.setState({ contractHash: contractHash[0].hash });
storehash.methods.setHash(this.state.contractHash).send({ from: accounts[0] }, (error, transactionHash) => {
console.log(transactionHash);
this.setState({transactionHash});
});
})
};
render() {
return (
<div className="app">
<h3> Choose file to send to IPFS </h3>
<form onSubmit={this.onSubmit}>
<input type="file" onChange={this.captureFile} />
<button type="submit">Submit</button>
</form>
<Document file={`https://ipfs.infura.io/ipfs/${this.state.contractHash}`} />
<a href={`https://ipfs.infura.io/ipfs/${this.state.contractHash}`}>Click to download the file</a>
<button onClick = {this.onClick}>Get Transaction Receipt</button>
<p>IPFS Hash: {this.state.contractHash}</p>
<p>Contract Address: {this.state.ethAddress}</p>
<p>Tx Hash: {this.state.transactionHash}</p>
</div>
);
}
}
export default App;
MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16 <0.7.0;
contract MyContract {
string contractHash;
function setHash(string memory ipfsHash) public {
contractHash = ipfsHash;
}
function getHash() public view returns (string memory ipfsHash) {
return contractHash;
}
}
I've looked at other solutions on SO but none that I found were particularly related to my question. Thank you for your help and time!
Two things to try:
Add ?filename= parameter as a hint for both gateway and react-pdf:
<Document src={`https://ipfs.infura.io/ipfs/${this.state.ipfshash}?filename=test.pdf`} />
This will make content-type returned by the gateways more reliable and eliminate false-negatives in react-pdf.
Run your own gateway, or contact Infura and discuss raising request limits for your app.
FYI I've run below test multiple times:
$ curl -Ls 'https://dweb.link/ipfs/QmUqB9dWDCeZ5nth9YKRJTQ6PcnfrGPPx1vzdyNWV6rh8s?filename=test.pdf' > output && file output
output: PDF document, version 1.5
After a few times they stop returning PDF, instead they return HTML page with 429 Too Many Requests error:
output: HTML document, ASCII text, with CRLF line terminators
$ cat output
<html>
<head><title>429 Too Many Requests</title></head>
<body>
<center><h1>429 Too Many Requests</h1></center>
<hr><center>openresty</center>
</body>
</html>
It is very likely that react-pdf is unable to render your PDF because it gets 429 Too Many Requests error response instead of the PDF payload.