How to set custom DropzoneJS progress value? - reactjs

I am using dropzone-react-component, which is simply a wrapper of DropzoneJS for react.
I added an event that handles uploading like this:
addedfile: file => handleFileUpload(file),
However, the upload progress bar simply goes up and validates the upload, while I can see that the upload is still taking place.
So my question, is, how am I supposed to set my own value for the upload percentage and make it work with the dropzone's own styling?
This is the React component:
constructor(props) {
super(props);
this.componentConfig = {
iconFiletypes: ['.jpg', '.png', '.gif', '.pdf'],
showFiletypeIcon: true,
postUrl: 'no-url',
};
this.eventHandlers = {
addedfile: file => handleFileUpload(file),
};
this.djsConfig = {
autoProcessQueue: false,
dictDefaultMessage: 'Déposez un fichier ici ou cliquez pour en choisir un',
maxFilesize: 200, // MB
clickable: true, // Lets you click the dropzone
acceptedFiles: 'image/*,application/pdf',
renameFileName: this.props.fileRename || 'myFile',
parallelUploads: 1,
uploadMultiple: false,
};
}
render() {
return (
<DropzoneComponent
config={this.componentConfig}
eventHandlers={this.eventHandlers}
djsConfig={this.djsConfig}
/>
}
And this is the code used to upload the file, which uses the package Meteor Slingshot:
const handleFileUpload = (file) => {
var uploader = new Slingshot.Upload("myFileUploads");
uploader.send(file, function (error, downloadUrl) {
if (error) {
// Log service detailed response.
console.log(error);
} else {
console.log(downloadUrl);
}
});
let computation = Tracker.autorun(() => {
if (!isNaN(uploader.progress())) {
console.log(uploader.progress());
}
});
};

Related

How to get the Blob image preview in my Uppy Custom setup

I learn React and now I use the Uppy so user can select files for upload.
When user have select his file the files are hidden by settting showSelectedFiles={false}
I use my own Component to show the selected files and I get the files using this:
.on("file-added", (file) => {
const { setFile } = props;
setFile(file);
const newList = this.state.files.concat({ file });
this.setState({
files: { newList },
});
});
For each file added to the Dashboard the setFile(file); is sending the file object to my Custom view. The problem is that the preview image Blob that is auto created by the Dashboard is not present at this stage.
How can I get the files to my Custom GUI to show them including the image preview Blob?
I'm new to React and JavaScript so please be gentle:)
Complete code:
import React from "react";
import "#uppy/status-bar/dist/style.css";
import "#uppy/drag-drop/dist/style.css";
import "#uppy/progress-bar/dist/style.css";
import "./styles.css";
import "#uppy/core/dist/style.css";
import "#uppy/dashboard/dist/style.css";
const Uppy = require("#uppy/core");
// const Dashboard = require("#uppy/dashboard");
const GoogleDrive = require("#uppy/google-drive");
const Dropbox = require("#uppy/dropbox");
const Instagram = require("#uppy/instagram");
const Webcam = require("#uppy/webcam");
const Tus = require("#uppy/tus");
const ThumbnailGenerator = require("#uppy/thumbnail-generator");
const {
Dashboard,
DashboardModal,
DragDrop,
ProgressBar,
} = require("#uppy/react");
class DashboardUppy extends React.Component {
constructor(props) {
super(props);
this.form = React.createRef();
this.state = {
showInlineDashboard: false,
open: false,
files: [],
};
this.uppy = new Uppy({
id: "uppy1",
autoProceed: false,
debug: true,
allowMultipleUploads: true,
proudlyDisplayPoweredByUppy: true,
restrictions: {
// maxFileSize: 1000000,
maxNumberOfFiles: 100,
minNumberOfFiles: 1,
allowedFileTypes: null,
},
onBeforeFileAdded: (currentFile, files) => {
console.log(files);
const modifiedFile = Object.assign({}, currentFile, {
name: currentFile + Date.now(),
});
if (!currentFile.type) {
// log to console
this.uppy.log(`Skipping file because it has no type`);
// show error message to the user
this.uppy.info(`Skipping file because it has no type`, "error", 500);
return false;
}
return modifiedFile;
},
})
.use(Tus, { endpoint: "https://master.tus.io/files/" })
.use(GoogleDrive, { companionUrl: "https://companion.uppy.io" })
.use(Dropbox, {
companionUrl: "https://companion.uppy.io",
})
.use(Instagram, {
companionUrl: "https://companion.uppy.io",
})
.use(Webcam, {
onBeforeSnapshot: () => Promise.resolve(),
countdown: false,
modes: ["video-audio", "video-only", "audio-only", "picture"],
mirror: true,
facingMode: "user",
locale: {
strings: {
// Shown before a picture is taken when the `countdown` option is set.
smile: "Smile!",
// Used as the label for the button that takes a picture.
// This is not visibly rendered but is picked up by screen readers.
takePicture: "Take a picture",
// Used as the label for the button that starts a video recording.
// This is not visibly rendered but is picked up by screen readers.
startRecording: "Begin video recording",
// Used as the label for the button that stops a video recording.
// This is not visibly rendered but is picked up by screen readers.
stopRecording: "Stop video recording",
// Title on the “allow access” screen
allowAccessTitle: "Please allow access to your camera",
// Description on the “allow access” screen
allowAccessDescription:
"In order to take pictures or record video with your camera, please allow camera access for this site.",
},
},
}).use(ThumbnailGenerator, {
thumbnailWidth: 200,
// thumbnailHeight: 200 // optional, use either width or height,
waitForThumbnailsBeforeUpload: true
})
.on("thumbnail:generated", (file, preview) => {
const img = document.createElement("img");
img.src = preview;
img.width = 100;
document.body.appendChild(img);
})
.on("file-added", (file) => {
const { setFile } = props;
setFile(file);
const newList = this.state.files.concat({ file });
this.setState({
files: { newList },
});
});
}
componentWillUnmount() {
this.uppy.close();
}
render() {
const { files } = this.state;
this.uppy.on("complete", (result) => {
console.log(
"Upload complete! We’ve uploaded these files:",
result.successful
);
});
return (
<div>
<div>
<Dashboard
uppy={this.uppy}
plugins={["GoogleDrive", "Webcam", "Dropbox", "Instagram"]}
metaFields={[
{ id: "name", name: "Name", placeholder: "File name" },
]}
open={this.state.open}
target={document.body}
onRequestClose={() => this.setState({ open: false })}
showSelectedFiles={false}
/>
</div>
</div>
);
}
}
export default DashboardUppy;
Ran into this problem as well because I wanted to use the image preview to figure out the aspect ratio of the underlying image.
If you're using Dashboard or ThumbnailGenerator for Uppy, an event is emitted for every upload:
uppy.on('thumbnail:generated', (file, preview) => {
const img = new Image();
img.src = preview;
img.onload = () => {
const aspect_ratio = img.width / img.height;
// Remove image if the aspect ratio is too weird.
// TODO: notify user.
if (aspect_ratio > 1.8) {
uppy.removeFile(file.id);
}
}
});
I realize though that you already are looking for this event in your code. I guess to answer your question, just put your logic there instead of in file-added.

How to use CustomRequest with antd upload function to upload images to firebase?

I'm trying to use the antd upload component and pictures wall example to upload images to my firebase storage.
Initially i tried using the action property as out lined here with the same results. I then tried using the customRequest form as outlined in the solution to that question. After struggling all day, I just can't seem to get it to work. Clearly I don't understand whats going on well enough.
My various change functions..
handleCancel = () => this.setState({ previewVisible: false });
handlePreview = async file => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
this.setState({
previewImage: file.url || file.preview,
previewVisible: true,
});
};
handleChange = (info) => {
console.log('handleChange',info);
if (info.file.status === 'uploading') {
console.log('setting loading to true');
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
console.log('setting loading to false');
getBase64(info.file.originFileObj, imageUrl => this.setState({
imageUrl,
loading: false
}));
}
};
my customupload function..
customUpload = async ({ onError, onSuccess, file }) => {
console.log("customUpload called");
console.log(file);
const storage = firebase.storage();
const metadata = {
contentType: 'image/jpeg'
};
const storageRef = await storage.ref();
// const imageName = generateHashName() //a unique name for the image
const imgFile = storageRef.child(`Property Photos/${file}.png`);
try {
const image = await imgFile.put(file, metadata);
onSuccess(null, image);
}
catch(e) {
onError(e);
};
};
my render JSX..
<Form.Item label="Photos">
<div className="clearfix">
<Upload
listType="picture-card"
fileList={fileList}
multiple={true}
accept="image"
onPreview={this.handlePreview}
onChange={this.handleChange}
customRequest={this.customUpload}
>
{this.imageUrl ? <img src={this.imageUrl} alt="avatar" /> : uploadButton}
</Upload>
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
</Form.Item>
Funny enough it runs and sort of works, but seems to have three issues..
When I upload an image(s) the card/box(s) shows "Upload" with the animated spinning wheel forever. It never completes and shows the image thumbnail.
When selecting mulitple images, only the first one seems to end up on firebase. Never more..
When selecting the image(s) to upload and clicking OK, there is a long pause (5 seconds?) like the application hangs before i can click anything again. not sure why that is.
It feels like i just don't understand how to use this customRequest property..
In general, what has to be done is: connect the "onProgress", "onSuccess" and "onError" callback functions of 'antd Upload' to the corresponding event observers of cloudStorage upload function.
https://firebase.google.com/docs/storage/web/upload-files#monitor_upload_progress
So, the customRequest function can be as:
let customRequest = ({
file,
onSuccess,
onError,
onProgress,
}) => {
var uploadTask = storageRef.child(`images/${file.name}`).put(file);
// Register three observers:
// 1. 'state_changed' observer, called any time the state changes
// 2. Error observer, called on failure
// 3. Completion observer, called on successful completion
uploadTask.on(
"state_changed",
function (snapshot) {
// Observe state change events such as progress, pause, and resume
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
var progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
// CONNECT ON PROGRESS
onProgress(progress)
switch (snapshot.state) {
case firebase.storage.TaskState.PAUSED: // or 'paused'
console.log("Upload is paused");
break;
case firebase.storage.TaskState.RUNNING: // or 'running'
console.log("Upload is running");
break;
}
},
function (error) {
// Handle unsuccessful uploads
// CONNECT ON ERROR
onError(error)
},
function () {
// Handle successful uploads on complete
// For instance, get the download URL: https://firebasestorage.googleapis.com/...
uploadTask.snapshot.ref
.getDownloadURL()
.then(function (downloadURL) {
console.log("File available at", downloadURL);
// CONNECT ON SUCCESS
onSuccess(downloadURL) // Pass any parameter you would like
});
}
);
};

unable to switch camera (front to rear) in react application using webrtc MediaDevices

This is the sample demonstration of what I'm intended to do.
If anyone has any idea about this fix to make it work or any new logic please do share.
This demonstration is implemented by using mediaStream API and
using react-webcam library which actually gives option to manage the camera view with the help of the props named videoConstraints={facingMode: 'user' or 'environment'} but it doesn't seems to be working.
when I click the camera switch ICON screen just hangs and nothing shows and also sometime it is working unexpectedly So ultimately I had to jumps to this native API solution which shows the code right below.
with all regards thanks in anticipation.
start() {
if (window.stream) {
console.log('found stream and clearing that', window.stream)
window.stream.getTracks().forEach(function(track) {
track.stop()
})
}
const constraints = {
video: true,
audio: false
}
return navigator.mediaDevices
.getUserMedia(constraints)
.then(this.gotStream)
.then(this.gotDevices)
.catch(this.handleError);
}
gotStream(stream) {
window.stream = stream // make stream available to console
// video.srcObject = stream;
// Refresh button list in case labels have become available
console.log('enumerating media devices ')
return navigator.mediaDevices.enumerateDevices()
}
gotDevices(mediaDevices) {
const { availableVideoInputs, videoConstraints } = this.state
mediaDevices.forEach(mediaDevice => {
// console.log(mediaDevice)
if (mediaDevice.kind === 'videoinput') {
console.log('found new video input ', mediaDevice)
availableVideoInputs.push({
deviceId: mediaDevice.deviceId,
label: mediaDevice.label
})
// availableVideoInputs.push('mediaDevice.deviceId.availableVideoInputs.push(mediaDevice.deviceId)')
}
})
console.log('aggregated availableVideoInputs new ', availableVideoInputs)
if (availableVideoInputs.length > 0) {
// there are accessible webcam
// setting first device as default to open
const tempVideoConstraint = {...videoConstraints}
if (availableVideoInputs[0].deviceId) {
console.log('availableVideoInputs[0] = ', availableVideoInputs[0])
tempVideoConstraint.deviceId = availableVideoInputs[0].deviceId
}
// console.log('putting tempVideoConstraint.facingMode ', tempVideoConstraint)
// if (availableVideoInputs[0].label.includes('back')) {
// tempVideoConstraint.facingMode = { exact: 'environment'}
// } else {
// // it is now turn to set front active
// tempVideoConstraint.facingMode = 'user'
// }
console.log('setting new video constrains ', tempVideoConstraint)
// this.setState({
// availableVideoInputs,
// // activeVideoInputID: availableVideoInputs[0].deviceId,
// // videoConstraints: tempVideoConstraint
// })
this.updateAvailableVideoStream(availableVideoInputs)
return Promise.resolve('done setting updateAvailableVideoStream')
} else {
// no webcam is available or accessible
console.error('ERR::VIDEO_STREAM_NOT_AVAILABLE')
}
}
updateAvailableVideoStream(availableVideoInputs) {
this.setState({ availableVideoInputs })
}
componentDidMount() {
this.start()
.then(data => {
console.log('data ', data)
console.log('update state ', this.state)
this.setState({
videoConstraints: {
...this.state.videoConstraints,
facingMode: 'user'
}
})
})
}
handleCameraSwitch() {
const { videoConstraints, availableVideoInputs, activeVideoInputID } = this.state
console.log('current video constraints ', videoConstraints)
const tempVideoConstraint = { ...videoConstraints }
// now check if it is possible to change camera view
// means check for another webcam
console.log({ availableVideoInputs })
console.log({ activeVideoInputID })
console.log({ remainingVideoStreams })
if (availableVideoInputs.length === 1) {
// cannot change the webcam as there is only 1 webcam available
console.error('ERR - cannot change camera view [Available Video Inputs: 1]')
return
}
// now change the view to another camera
// get the current active video input device id and filter then from available video stream
const remainingVideoStreams = availableVideoInputs.filter(videoStream => videoStream.deviceId !== activeVideoInputID)
// now check if in remainingVideoStreams there is more than 1 stream available to switch
// if available then show the Stream Selection List to user
// else change the stream to remainingVideoStreams[0]
console.log({ availableVideoInputs })
console.log({ activeVideoInputID })
console.log({ remainingVideoStreams })
if (remainingVideoStreams && remainingVideoStreams.length === 1) {
tempVideoConstraint.deviceId = remainingVideoStreams[0].deviceId
console.log('new video constraints ', {...tempVideoConstraint})
console.log('webcam ref ', this.webCamRef.current)
// if (remainingVideoStreams[0].label.includes('back') || tempVideoConstraint.facingMode === 'user') {
// tempVideoConstraint.facingMode = { exact: 'environment' }
// } else {
// // it is now turn to set front active
// tempVideoConstraint.facingMode = 'user'
// }
console.log('new video constraints with facing mode', tempVideoConstraint)
// const constraints = {
// video: tempVideoConstraint
// }
// navigator.mediaDevices.getUserMedia(constraints)
// .then((stream) => {
// console.log('stream -> ', stream)
// })
// .catch((error) => {
// console.error('Some error occured while changing the camera view ', error)
// console.log(error)
// })
this.setState({ videoConstraints: tempVideoConstraint, activeVideoInputID: remainingVideoStreams[0].deviceId })
} else {
// show the remaining stream list to user
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
This is the little variation of your Implementation.
But this will work exactly you've wished for.
Please see the below implementation for switching the camera front/back.
I have also added error validation like:
It will throw an error if there is no video stream available.
It will throw an error if there is only 1 video stream available when trying to access back camera.
Please do like and comment back if you have any other approach or want more clarification
componentDidMount() {
const gotDevices = (mediaDevices) =>
new Promise((resolve, reject) => {
const availableVideoInputs = []
mediaDevices.forEach(mediaDevice => {
if (mediaDevice.kind === 'videoinput') {
availableVideoInputs.push({
deviceId: mediaDevice.deviceId,
label: mediaDevice.label
})
}
})
if (availableVideoInputs.length > 0) {
resolve(availableVideoInputs)
} else {
reject(new Error('ERR::NO_MEDIA_TO_STREAM'))
}
})
navigator.mediaDevices.enumerateDevices().then(gotDevices)
.then((availableVideoInputs) => this.setState({ availableVideoInputs }))
.catch((err) => this.setState({ hasError: err }))
}
updateFileUploadView(newActiveView) {
this.setState({ activeFileUploadView: newActiveView })
const { hasError } = this.state
if (newActiveView === 'clickFromWebcam' && hasError) {
return console.error(hasError)
}
if (newActiveView === '') {
// means no view is active and clear the selected image
this.setState({ captureImageBase64: '', videoConstraints: defaultVideoConstraints })
}
}
changeCameraView() {
const { availableVideoInputs } = this.state
if (availableVideoInputs.length === 1) {
return console.error('ERR::AVAILABLE_MEDIA_STREAMS_IS_1')
}
this.setState({ resetCameraView: true })
setTimeout(() => {
const { videoConstraints: { facingMode } } = this.state
const newFacingMode = facingMode === 'user' ? { exact: 'environment' } : 'user'
this.setState({
videoConstraints: { facingMode: newFacingMode },
resetCameraView: false
})
}, 100)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
!resetCameraView ?
<Webcam
audio={false}
height='100%'
ref={this.webCamRef}
screenshotFormat="image/png"
minScreenshotWidth={screenShotWidth}
minScreenshotHeight={screenShotHeight}
screenshotQuality={1}
width='100%'
videoConstraints={videoConstraints}
/>
: 'Loading...'
As you can see this implementation is using react-webcam library
In componentDidMount you will first check for the available media stream of kind video input, then in other methods like changing cameraView i.e switching the camera to front/back.
I'm unmounting Webcam for 100ms only and then mounting it back with new videoConstraints either { facingMode: 'user' } or { facingMode: { exact: 'environment' } }
This approach will give your code a head start and you can play around the code and have fun.
Thank you!

File upload progress using react-dropzone

Using react-dropzone to upload the file, I want to achieve the file progress like in percentage of file transfer or mbs data transfer.
Here is the link of: https://react-dropzone.netlify.com/
onDrop(acceptedFiles, uploadApi) {
const filesToBeSent = this.state.filesToBeSent;
if (acceptedFiles.length) {
if (acceptedFiles[0].type === FileTypeList.TYPE) {
filesToBeSent.push(acceptedFiles);
const formData = new FormData();
formData.append("file", acceptedFiles[0]);
uploadApi(formData).then((response) => {
this.setState({
filesPreview: [],
filesToBeSent: [{}],
showNotification: true,
uploadResponse: response,
});
this.props.fetchHistory();
});
} else {
this.setState({
fileType: true,
});
}
} else {
this.setState({
fileSize: true,
});
}
}
<Dropzone maxSize={this.props.maxSize} onDrop={(files) => this.onDrop(files, this.props.uploadApi)}>
{({ getRootProps, getInputProps }) => {
return (
<div {...getRootProps()} className={"dropzone"}>
<UploadPanel id="uploadFileContainerId">
<p>
<img id="uploadImage" src={UploadImage} />
</p>
<input {...getInputProps()} />
<div>{t("assets:UPLOAD_FILE")}</div>
<Note>
{this.props.maxSizeTitle ? t("workers:UPLOAD_WORKER_FILE_SIZE") : t("assets:UPLOAD_FILE_SIZE")}
</Note>
</UploadPanel>
</div>
);
}}
</Dropzone>
In case you wanna detect file upload process you can use XMLHttpRequest
onDrop(acceptedFiles) {
const formData = new FormData();
formData.append('file', acceptedFiles[0])
const xhr = new XMLHttpRequest();
xhr.open(/*params*/);
xhr.send(formData)
xhr.upload.onprogress = event => {
const percentages = +((event.loaded / event.total) * 100).toFixed(2);
this.setState({percentages})
};
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
if (xhr.status !== 200) {
/*handle error*/
}
/*handle success*/
};
}
You can use React Dropzone Uploader, which gives you file previews with upload progress out of the box, and also handles uploads for you.
import 'react-dropzone-uploader/dist/styles.css'
import Dropzone from 'react-dropzone-uploader'
const Uploader = () => {
return (
<Dropzone
getUploadParams={() => ({ url: 'https://httpbin.org/post' })} // specify upload params and url for your files
onChangeStatus={({ meta, file }, status) => { console.log(status, meta, file) }}
onSubmit={(files) => { console.log(files.map(f => f.meta)) }}
accept="image/*,audio/*,video/*"
/>
)
}
Uploads can be cancelled or restarted. The UI is fully customizable.
Full disclosure: I wrote this library to address some of the shortcomings and excessive boilerplate required by React Dropzone.
Here is another example based on turchak's answer for handling any number of files:
onDrop(acceptedFiles) {
const formData = new FormData();
for (const file of acceptedFiles) formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = event => {
const percentage = parseInt((event.loaded / event.total) * 100);
console.log(percentage); // Update progress here
};
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
if (xhr.status !== 200) {
console.log('error'); // Handle error here
}
console.log('success'); // Handle success here
};
xhr.open('POST', 'https://httpbin.org/post', true);
xhr.send(formData);
}

multiple image upload one by one with delete and change option with preview

I am trying to upload image one by one with change and delete option(for each image uploaded) in multiple view with react, apollo client. But with this I can't get the clear thought about how to perform this easily and confused a lot..
Please anyone help me to get rid of this...
**updated**
Hi now i am using react-dropzonecomponent so far, but here i did mutiple file upload with delete option only..
Here i can send the files to server(node using mulitpart form data), in DB create the file in server end and store the path in database with path name only... But here i can't show the image files in front end from the path got from Back end...
const initialState = {
files: [],
imagePreviewUrl: []
};
class Image extends React.Component {
constructor(props) {
super(props);
this.state = { ...initialState };
}
componentWillMount() {
let {match, data} = this.props;
const id = match.params.id && match.params.id.slice(1);
if (id) {
let currentProduct = (data && data.getProduct) && data.getProduct.find((data) => {
return data.id == id;
});
this.setState({
imagePreviewUrl: currentProduct.images
});
}
}
handleAdd(file) {
console.log(file)
var allFiles = this.state.files;
allFiles = allFiles.concat([file]);
this.setState({
files: allFiles
});
}
handleRemove(file) {
let allFiles = this.state.files;
this.state.files.forEach((itr, i) => {
if (itr.upload.uuid == file.upload.uuid) {
allFiles.splice(i, 1)
}
});
this.setState({
files: allFiles
});
console.log(this.state.files, allFiles, file)
}
render() {
let {match, classes, data} = this.props;
let {imagePreviewUrl} = this.state;
const id = match.params.id && match.params.id.slice(1);
var self = this;
return (
<GridContainer>
<DropzoneComponent
config={{
postUrl: 'no-url',
iconFiletypes: ['.jpg', '.png', '.gif'],
showFiletypeIcon: true
}}
eventHandlers=
{{
addedfile: (file) => this.handleAdd(file),
removedfile: (file) => this.handleRemove(file),
init: (dropzone) => {
console.log(dropzone)
}
}}
djsConfig={{
autoProcessQueue: false,
addRemoveLinks: true,
previewTemplate: ReactDOMServer.renderToStaticMarkup(
...<img data-dz-thumbnail="true" /> ...)}} />
</GridContainer>
);
}
}
export default withStyles(style)(Image);

Resources