Skip to content

Commit bf4e44b

Browse files
designcodeXe
andauthored
feat: initial docs for tigris sdk (#262)
* feat: initial docs for tigris sdk --- Signed-off-by: Xe Iaso <[email protected]> Co-authored-by: Xe Iaso <[email protected]>
1 parent 3c7ac23 commit bf4e44b

File tree

7 files changed

+922
-0
lines changed

7 files changed

+922
-0
lines changed

docs/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { YellowStar } from "@site/src/icons";
1111
import SDKCards from "@site/src/components/SDKCards";
1212
import WorldMap from "@site/static/img/world.png";
13+
import TigrisStorageSDK from "@site/static/img/tigris-sdk.png";
1314
import Link from "@docusaurus/Link";
1415

1516
<div style={{ textAlign: 'center', margin: "2em auto 0 auto", width: '80%' }}>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Client Uploads
2+
3+
Amongst all the other great features of Tigris, free egress fees is another
4+
example of what makes us stand out from other providers. We care about the
5+
bandwidth costs and we want to make it as cheap as possible for you to use
6+
Tigris. That's why we've made it so that you can upload files directly to Tigris
7+
from the client side.
8+
9+
We leverage the
10+
[presigned URLs](/docs/sdks/tigris/using-sdk#presigning-an-object) features to
11+
allow you to upload files directly to Tigris from the client side.
12+
13+
Client side uploads are a great way to upload objects to a bucket directly from
14+
the browser as it allows you to upload objects to a bucket without having to
15+
proxy the objects through your server saving costs on bandwidth.
16+
17+
## Uploading an object
18+
19+
You can use the `upload` method from `client` package to upload objects directly
20+
to Tigris from the client side.
21+
22+
```ts
23+
import { upload } from "@tigrisdata/storage/client";
24+
```
25+
26+
`upload` accepts the following parameters:
27+
28+
- `path`: (Required) A string specifying the path to the object
29+
- `body`: (Required) A blob object as File or Blob
30+
- `options`: (Optional) A JSON object with the following optional parameters:
31+
32+
#### `options`
33+
34+
| **Parameter** | **Required** | **Values** |
35+
| ---------------- | ------------ | ----------------------------------------------------------------------------------------------------------- |
36+
| url | No | The URL to upload the file to. |
37+
| access | No | The access level for the object. Possible values are `public` and `private`. |
38+
| onUploadProgress | No | Callback to track upload progress: `onUploadProgress({loaded: number, total: number, percentage: number})`. |
39+
| config | No | A configuration object to override the [default configuration](/docs/sdks/tigris/using-sdk#authentication). |
40+
41+
### Example
42+
43+
```html
44+
<input type="file" onchange="handleFileChange(event)" />
45+
46+
<script>
47+
function handleFileChange(event) {
48+
const file = event.target.files[0];
49+
upload("file.txt", file, {
50+
url: "/api/upload",
51+
access: "private",
52+
onUploadProgress: ({ loaded, total, percentage }) => {
53+
console.log(`Uploaded ${loaded} of ${total} bytes (${percentage}%)`);
54+
},
55+
});
56+
}
57+
</script>
58+
```
59+
60+
You can see a full example [here](/docs/sdks/tigris/examples#client-uploads).

docs/sdks/tigris/examples.mdx

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import Tabs from "@theme/Tabs";
2+
import TabItem from "@theme/TabItem";
3+
4+
# Examples
5+
6+
If you want to see it the Storage SDK used with your tool of choice, we have
7+
some ready examples available at
8+
[our community repo](https://github.com/tigrisdata-community/storage-sdk-examples).
9+
Something missing there that you you'd like to see? Let us know and we'll be
10+
more than happy to add in examples.
11+
12+
### Viewing and downloading files
13+
14+
`get` function can be used to get a file from a bucket. `contentDisposition`
15+
option can be to either `attachment` or `inline` depending on whether you want
16+
to trigger a download or display the file in the browser.
17+
18+
<Tabs>
19+
<TabItem value="server" label="Server" default>
20+
21+
```ts
22+
// app/api/avatar/route.ts
23+
import { NextRequest, NextResponse } from "next/server";
24+
import { get } from "@tigrisdata/storage";
25+
26+
export async function GET(
27+
req: NextRequest,
28+
): Promise<NextResponse<File | string>> {
29+
const avatar = req.nextUrl.searchParams.get("avatar");
30+
31+
if (!avatar) {
32+
return NextResponse.json("avatar parameter is required", { status: 400 });
33+
}
34+
35+
try {
36+
const avatarPath = decodeURIComponent(avatar as string);
37+
38+
const file = await get(avatarPath, "file", {
39+
contentDisposition: "inline",
40+
});
41+
42+
if (file.data) {
43+
return new NextResponse(file.data, { status: 200 });
44+
}
45+
46+
if (file.error && file.error.message) {
47+
return NextResponse.json(file.error.message, { status: 500 });
48+
}
49+
} catch (error) {
50+
return NextResponse.json(
51+
error instanceof Error ? error.message : "Unknown error",
52+
{ status: 500 },
53+
);
54+
}
55+
56+
return NextResponse.json("No data found", { status: 404 });
57+
}
58+
```
59+
60+
</TabItem>
61+
<TabItem value="client" label="Client">
62+
63+
```tsx
64+
import Image from "next/image";
65+
66+
export default function Avatar() {
67+
const { user } = getUserData();
68+
69+
return (
70+
<Image
71+
src={`/api/avatar?avatar=${encodeURIComponent(user.avatar)}`}
72+
alt="Avatar"
73+
width={100}
74+
height={100}
75+
/>
76+
);
77+
}
78+
```
79+
80+
</TabItem>
81+
</Tabs>
82+
83+
To trigger a download, set the `contentDisposition` option to `attachment`.
84+
85+
### Uploading files
86+
87+
`put` function can be used to upload a file to a bucket.
88+
89+
<Tabs>
90+
<TabItem value="server" label="Server" default>
91+
92+
```ts
93+
// app/api/upload/route.ts
94+
import { NextRequest, NextResponse } from "next/server";
95+
import { put } from "@tigrisdata/storage";
96+
97+
export async function PUT(req: NextRequest) {
98+
const formData = await req.formData();
99+
const file = formData.get("file") as File;
100+
101+
if (!file) {
102+
return NextResponse.json({ error: "No file provided" }, { status: 400 });
103+
}
104+
105+
const result = await put(file.name, file, {
106+
access: "public",
107+
addRandomSuffix: false,
108+
});
109+
110+
if (result.error) {
111+
return NextResponse.json({ error: result.error.message }, { status: 500 });
112+
}
113+
114+
return NextResponse.json({ data: result.data }, { status: 200 });
115+
}
116+
```
117+
118+
</TabItem>
119+
<TabItem value="client" label="Client">
120+
121+
```tsx
122+
export default function Upload() {
123+
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
124+
const file = e.target.files?.[0];
125+
126+
if (file) {
127+
const formData = new FormData();
128+
formData.append("file", file);
129+
fetch("/api/test", {
130+
method: "PUT",
131+
body: formData,
132+
});
133+
}
134+
};
135+
136+
return <input type="file" onChange={handleFileChange} />;
137+
}
138+
```
139+
140+
</TabItem>
141+
</Tabs>
142+
143+
### Client Uploads
144+
145+
Tigris does not charge egress fees, but your hosting provider may charge them
146+
for user uploads to Tigris. We care about your bandwidth costs, so we made it
147+
easy to have the server and client work together to upload things. This lets you
148+
give a presigned URL to the client and then have the client upload to Tigris
149+
instead of your server needing to be in the middle.
150+
151+
We leverage the
152+
[presigned URLs](/docs/sdks/tigris/using-sdk#presigning-an-object) features to
153+
allow you to upload files directly to Tigris from the client side.
154+
155+
<Tabs>
156+
<TabItem value="server" label="Server" default>
157+
158+
```ts
159+
// app/api/upload/route.ts
160+
import { NextRequest, NextResponse } from "next/server";
161+
import { getPresignedUrl } from "@tigrisdata/storage";
162+
163+
export async function POST(request: NextRequest) {
164+
try {
165+
const { path, method, contentType } = await request.json();
166+
const result = await getPresignedUrl(path, {
167+
method,
168+
contentType,
169+
expiresIn: 3600, // 1 hour
170+
});
171+
172+
return NextResponse.json({ data: result.data });
173+
} catch (error) {
174+
console.error("Upload error:", error);
175+
return NextResponse.json(
176+
{ error: "Failed to generate presigned URL" },
177+
{ status: 500 },
178+
);
179+
}
180+
}
181+
```
182+
183+
</TabItem>
184+
185+
<TabItem value="client" label="Client">
186+
187+
```tsx
188+
"use client";
189+
190+
import { upload } from "@tigrisdata/storage/client";
191+
import { useState } from "react";
192+
193+
export default function ClientUpload() {
194+
const [progress, setProgress] = useState<number>(0);
195+
const [url, setUrl] = useState<string | null>(null);
196+
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
197+
const file = e.target.files?.[0];
198+
setProgress(0);
199+
if (file) {
200+
const result = await upload(`${file.name}`, file, {
201+
url: "/api/upload",
202+
access: "private",
203+
onUploadProgress: ({ loaded, total, percentage }) => {
204+
setProgress(percentage);
205+
if (percentage === 100) {
206+
setProgress(0);
207+
}
208+
},
209+
});
210+
setUrl(result.url);
211+
}
212+
};
213+
214+
return (
215+
<>
216+
<input type="file" onChange={handleFileChange} />{" "}
217+
{url && <div>Uploaded to: {url}</div>}{" "}
218+
{progress > 0 && progress < 100 && <div>{progress}%</div>}{" "}
219+
</>
220+
);
221+
}
222+
```
223+
224+
</TabItem>
225+
226+
</Tabs>

0 commit comments

Comments
 (0)