├── .env.example
├── src
├── react-app-env.d.ts
├── index.tsx
├── index.css
├── components
│ ├── Loader.tsx
│ └── Upload.tsx
├── App.tsx
└── App.css
├── public
├── robots.txt
├── favicon.ico
├── manifest.json
└── index.html
├── images
└── demo.png
├── README.md
├── .gitignore
├── tsconfig.json
└── package.json
/.env.example:
--------------------------------------------------------------------------------
1 | CLIENT_ID=
2 | CLIENT_SECRET=
3 | REFRESH_TOKEN=
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/images/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/innovatorved/react-googledrive-upload/HEAD/images/demo.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/innovatorved/react-googledrive-upload/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | const root = ReactDOM.createRoot(
7 | document.getElementById("root") as HTMLElement
8 | );
9 | root.render(
10 |
11 |
12 |
13 | );
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Upload Image on Google Drive
2 |
3 | - React project for uploading a image on drive
4 |
5 | ## Demo Image
6 |
7 | 
8 |
9 | _.env_
10 |
11 | ```env
12 | # Add Your Access Refeshtoken , ClientId and ClientSecret
13 | CLIENT_ID=
14 | CLIENT_SECRET=
15 | REFRESH_TOKEN=
16 | ```
17 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Image Upload",
3 | "name": "Google Drive Image Upload App",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/Loader.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default function Loader() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | )
32 | }
33 |
34 |
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "drive-upload",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@react-oauth/google": "^0.5.0",
7 | "@testing-library/jest-dom": "^5.16.5",
8 | "@testing-library/react": "^13.4.0",
9 | "@testing-library/user-event": "^13.5.0",
10 | "@types/jest": "^27.5.2",
11 | "@types/node": "^16.18.3",
12 | "@types/react": "^18.0.25",
13 | "@types/react-dom": "^18.0.9",
14 | "googleapis": "^109.0.1",
15 | "react": "^18.2.0",
16 | "react-dom": "^18.2.0",
17 | "react-google-login": "^5.2.2",
18 | "react-scripts": "5.0.1",
19 | "typescript": "^4.9.3",
20 | "web-vitals": "^2.1.4"
21 | },
22 | "scripts": {
23 | "start": "react-scripts start",
24 | "build": "react-scripts build",
25 | "test": "react-scripts test",
26 | "eject": "react-scripts eject"
27 | },
28 | "eslintConfig": {
29 | "extends": [
30 | "react-app",
31 | "react-app/jest"
32 | ]
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.2%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 1 chrome version",
42 | "last 1 firefox version",
43 | "last 1 safari version"
44 | ]
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
25 | Google Drive Image upload
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import "./App.css";
3 |
4 | import Upload from "./components/Upload";
5 | import Loader from "./components/Loader";
6 |
7 | function App() {
8 | const CLIENT_ID = process.env.CLIENT_ID;
9 | const CLIENT_SECRET = process.env.CLIENT_SECRET;
10 | const REFRESH_TOKEN = process.env.REFRESH_TOKEN;
11 |
12 | const [token, setToken] = useState("");
13 | const [loading, setLoading] = useState(false);
14 |
15 | useEffect(() => {
16 | fetch("https://www.googleapis.com/oauth2/v4/token", {
17 | method: "POST",
18 | headers: new Headers({ "Content-Type": "application/json" }),
19 | body: JSON.stringify({
20 | client_id: CLIENT_ID,
21 | client_secret: CLIENT_SECRET,
22 | refresh_token: REFRESH_TOKEN,
23 | grant_type: "refresh_token",
24 | }),
25 | })
26 | .then(async (res: any) => {
27 | res = await res.json();
28 | setToken(res.access_token);
29 | })
30 | .catch((err) => alert(err.message));
31 | // eslint-disable-next-line
32 | }, [1]);
33 |
34 | return (
35 |
36 |
39 |
40 | {loading === false ? (
41 |
42 | ) : (
43 |
44 | )}
45 |
46 |
47 | );
48 | }
49 |
50 | export default App;
51 |
--------------------------------------------------------------------------------
/src/components/Upload.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function Upload(props: any) {
4 | const { token, setLoading } = props;
5 |
6 | const Uploadfile = async (e: React.ChangeEvent) => {
7 | if (token === "") {
8 | alert("Token not Found");
9 | return;
10 | }
11 | const files = e.target.files;
12 | if (files === null) return;
13 | if (Object.keys(files).length <= 0) {
14 | return;
15 | }
16 | const file = files[0];
17 | if (file.size > 10485761) {
18 | alert("File Size is Greater than 10MB");
19 | return;
20 | }
21 | const fileMetadata = { name: file.name };
22 | const form = new FormData();
23 | form.append(
24 | "metadata",
25 | new Blob([JSON.stringify(fileMetadata)], { type: "application/json" })
26 | );
27 | form.append("file", file);
28 | setLoading(true);
29 | fetch(
30 | "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart",
31 | {
32 | method: "POST",
33 | headers: new Headers({ Authorization: "Bearer " + token }),
34 | body: form,
35 | }
36 | )
37 | .then((res) => res.json())
38 | .then((res) => {
39 | console.log(res);
40 | setLoading(false);
41 | alert("Photo Uploaded to Drive");
42 | })
43 | .catch((err) => {
44 | setLoading(false);
45 | alert(err.message);
46 | });
47 | };
48 |
49 | return (
50 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App-header {
2 | background-color: #EDEDED;
3 | min-height: 90vh;
4 | display: flex;
5 | flex-direction: column;
6 | align-items: center;
7 | justify-content: center;
8 | color: white;
9 | }
10 |
11 | nav {
12 | text-align: center;
13 | font-family: 'Times New Roman', Times, serif;
14 | margin: 20px;
15 | color: #3d3c42;
16 | }
17 |
18 | form {
19 | margin: 0 auto;
20 | padding: 20px;
21 | width: 400px;
22 | height: auto;
23 | overflow: hidden;
24 | background: #FFFFFF;
25 | border-radius: 5px;
26 | }
27 |
28 | label {
29 | margin: 0;
30 | padding: 5px;
31 | width: auto;
32 | max-width: 200px;
33 | height: auto;
34 | font-family: 'Times New Roman', Times, serif;
35 | background-color: #3D3C42;
36 | border: none;
37 | color: #fff;
38 | cursor: pointer;
39 | text-align: center;
40 | border-radius: 2px;
41 | -webkit-border-radius: 2px;
42 | -webkit-transition: 0.2s ease all;
43 | -moz-transition: 0.2s ease all;
44 | -ms-transition: 0.2s ease all;
45 | -o-transition: 0.2s ease all;
46 | transition: 0.2s ease all;
47 | }
48 |
49 | label:hover {
50 | background: #ADADAD;
51 | }
52 |
53 | [type=file] {
54 | display: none;
55 | }
56 |
57 | @keyframes ldio-9u8gbddmqad {
58 | 0% {
59 | opacity: 1;
60 | }
61 |
62 | 100% {
63 | opacity: 0;
64 | }
65 | }
66 |
67 | .ldio-9u8gbddmqad div {
68 | left: 75.64999999999999px;
69 | top: 20.4px;
70 | position: absolute;
71 | animation: ldio-9u8gbddmqad linear 1.4492753623188404s infinite;
72 | background: #d5e5e8;
73 | width: 18.7px;
74 | height: 47.6px;
75 | border-radius: 3.8080000000000003px / 3.8080000000000003px;
76 | transform-origin: 9.35px 64.6px;
77 | }
78 |
79 | .ldio-9u8gbddmqad div:nth-child(1) {
80 | transform: rotate(0deg);
81 | animation-delay: -1.3833992094861658s;
82 | background: #d5e5e8;
83 | }
84 |
85 | .ldio-9u8gbddmqad div:nth-child(2) {
86 | transform: rotate(16.363636363636363deg);
87 | animation-delay: -1.3175230566534912s;
88 | background: #d5e5e8;
89 | }
90 |
91 | .ldio-9u8gbddmqad div:nth-child(3) {
92 | transform: rotate(32.72727272727273deg);
93 | animation-delay: -1.2516469038208167s;
94 | background: #d5e5e8;
95 | }
96 |
97 | .ldio-9u8gbddmqad div:nth-child(4) {
98 | transform: rotate(49.09090909090909deg);
99 | animation-delay: -1.185770750988142s;
100 | background: #d5e5e8;
101 | }
102 |
103 | .ldio-9u8gbddmqad div:nth-child(5) {
104 | transform: rotate(65.45454545454545deg);
105 | animation-delay: -1.1198945981554675s;
106 | background: #d5e5e8;
107 | }
108 |
109 | .ldio-9u8gbddmqad div:nth-child(6) {
110 | transform: rotate(81.81818181818181deg);
111 | animation-delay: -1.054018445322793s;
112 | background: #d5e5e8;
113 | }
114 |
115 | .ldio-9u8gbddmqad div:nth-child(7) {
116 | transform: rotate(98.18181818181819deg);
117 | animation-delay: -0.9881422924901185s;
118 | background: #d5e5e8;
119 | }
120 |
121 | .ldio-9u8gbddmqad div:nth-child(8) {
122 | transform: rotate(114.54545454545455deg);
123 | animation-delay: -0.9222661396574439s;
124 | background: #d5e5e8;
125 | }
126 |
127 | .ldio-9u8gbddmqad div:nth-child(9) {
128 | transform: rotate(130.9090909090909deg);
129 | animation-delay: -0.8563899868247693s;
130 | background: #d5e5e8;
131 | }
132 |
133 | .ldio-9u8gbddmqad div:nth-child(10) {
134 | transform: rotate(147.27272727272728deg);
135 | animation-delay: -0.7905138339920947s;
136 | background: #d5e5e8;
137 | }
138 |
139 | .ldio-9u8gbddmqad div:nth-child(11) {
140 | transform: rotate(163.63636363636363deg);
141 | animation-delay: -0.7246376811594202s;
142 | background: #d5e5e8;
143 | }
144 |
145 | .ldio-9u8gbddmqad div:nth-child(12) {
146 | transform: rotate(180deg);
147 | animation-delay: -0.6587615283267456s;
148 | background: #d5e5e8;
149 | }
150 |
151 | .ldio-9u8gbddmqad div:nth-child(13) {
152 | transform: rotate(196.36363636363637deg);
153 | animation-delay: -0.592885375494071s;
154 | background: #d5e5e8;
155 | }
156 |
157 | .ldio-9u8gbddmqad div:nth-child(14) {
158 | transform: rotate(212.72727272727272deg);
159 | animation-delay: -0.5270092226613965s;
160 | background: #d5e5e8;
161 | }
162 |
163 | .ldio-9u8gbddmqad div:nth-child(15) {
164 | transform: rotate(229.0909090909091deg);
165 | animation-delay: -0.46113306982872193s;
166 | background: #d5e5e8;
167 | }
168 |
169 | .ldio-9u8gbddmqad div:nth-child(16) {
170 | transform: rotate(245.45454545454547deg);
171 | animation-delay: -0.39525691699604737s;
172 | background: #d5e5e8;
173 | }
174 |
175 | .ldio-9u8gbddmqad div:nth-child(17) {
176 | transform: rotate(261.8181818181818deg);
177 | animation-delay: -0.3293807641633728s;
178 | background: #d5e5e8;
179 | }
180 |
181 | .ldio-9u8gbddmqad div:nth-child(18) {
182 | transform: rotate(278.1818181818182deg);
183 | animation-delay: -0.26350461133069825s;
184 | background: #d5e5e8;
185 | }
186 |
187 | .ldio-9u8gbddmqad div:nth-child(19) {
188 | transform: rotate(294.54545454545456deg);
189 | animation-delay: -0.19762845849802368s;
190 | background: #d5e5e8;
191 | }
192 |
193 | .ldio-9u8gbddmqad div:nth-child(20) {
194 | transform: rotate(310.90909090909093deg);
195 | animation-delay: -0.13175230566534912s;
196 | background: #d5e5e8;
197 | }
198 |
199 | .ldio-9u8gbddmqad div:nth-child(21) {
200 | transform: rotate(327.27272727272725deg);
201 | animation-delay: -0.06587615283267456s;
202 | background: #d5e5e8;
203 | }
204 |
205 | .ldio-9u8gbddmqad div:nth-child(22) {
206 | transform: rotate(343.6363636363636deg);
207 | animation-delay: 0s;
208 | background: #d5e5e8;
209 | }
210 |
211 | .loadingio-spinner-spinner-d5vda8qm7j5 {
212 | width: 170px;
213 | height: 170px;
214 | display: inline-block;
215 | overflow: hidden;
216 | background: none;
217 | }
218 |
219 | .ldio-9u8gbddmqad {
220 | width: 100%;
221 | height: 100%;
222 | position: relative;
223 | transform: translateZ(0) scale(1);
224 | backface-visibility: hidden;
225 | transform-origin: 0 0;
226 | /* see note above */
227 | }
228 |
229 | .ldio-9u8gbddmqad div {
230 | box-sizing: content-box;
231 | }
232 |
233 |
234 |
235 | @keyframes App-logo-spin {
236 | from {
237 | transform: rotate(0deg);
238 | }
239 |
240 | to {
241 | transform: rotate(360deg);
242 | }
243 | }
--------------------------------------------------------------------------------