@bytescale/upload-widget-vue
Version:
Vue File Upload UI Widget โ Lightweight & supports: drag and drop, multiple uploads, image cropping, customization & more ๐ Comes with Cloud Storage ๐
556 lines (446 loc) โข 21.6 kB
Markdown
<h1 align="center">
<a href="https://www.bytescale.com/docs/upload-widget/vue">
<img alt="Bytescale Upload Widget for Vue" width="524" height="80" src="https://raw.githubusercontent.com/bytescale/bytescale-upload-widget-vue/main/.github/assets/bytescale-upload-widget-vue.svg">
</a>
</h1>
<p align="center"><b>Beautiful File Upload Widget for Vue</b><br/> (With Built-in Cloud Storage)</p>
<br/>
<p align="center">
<a href="https://www.npmjs.com/package/@bytescale/upload-widget-vue">
<img src="https://img.shields.io/badge/%40bytescale%2Fupload--widget--vue-npm-4ba0f6" />
</a>
<a href="https://github.com/bytescale/bytescale-upload-widget-vue/actions/workflows/ci.yml">
<img src="https://img.shields.io/badge/build-passing-4ba0f6" />
</a>
<a href="https://github.com/bytescale/bytescale-upload-widget-vue/">
<img src="https://img.shields.io/badge/gzipped-33%20kb-4ba0f6" />
</a>
<a href="https://www.npmjs.com/package/@bytescale/upload-widget-vue">
<img src="https://img.shields.io/npm/dt/uploader?color=%234ba0f6" />
</a>
<br/>
<a href="https://www.npmjs.com/package/@bytescale/upload-widget-vue">
<img src="https://img.shields.io/badge/TypeScript-included-4ba0f6" />
</a>
<a href="https://github.com/bytescale/bytescale-upload-widget-vue/actions/workflows/ci.yml">
<img src="https://img.shields.io/npms-io/maintenance-score/uploader?color=4ba0f6" />
</a>
<a target="_blank" href="https://twitter.com/intent/tweet?text=This%20was%20a%20great%20find...%0A%0Ahttps%3A%2F%2Fgithub.com%2Fbytescale%2Fbytescale-upload-widget-vue">
<img alt="Twitter URL" src="https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2Fbytescale%2Fbytescale-upload-widget-vue" />
</a>
</p>
<h1 align="center">
Get Started โ
<a href="https://codepen.io/bytescale/pen/yLpvYew?editors=0010">
Try on CodePen
</a>
</h1>
<p align="center"><a href="https://www.bytescale.com/docs/upload-widget"><img alt="Upload Widget Demo" width="100%" src="https://raw.githubusercontent.com/bytescale/bytescale-upload-widget/main/.github/assets/demo.gif"></a></p>
<p align="center">100% Serverless File Upload Widget <br /> Powered by <a href="https://www.bytescale.com/">Bytescale</a><br/><br/></p>
<hr/>
<p align="center">
<b>Supports:</b> Image Cropping, Video Previews, Document Previews, Drag & Drop, and more...
<br />
<br />
<a href="https://www.bytescale.com/docs/upload-widget/vue" rel="nofollow"><b>Full Documentation</b></a> โข <a href="https://www.bytescale.com/docs/sdks/javascript" rel="nofollow">Headless SDK</a> โข <a href="https://www.bytescale.com/docs/media-processing-apis" rel="nofollow">Media Processing APIs</a> โข <a href="https://www.bytescale.com/docs/storage/sources" rel="nofollow">Storage</a> โข <a href="https://www.bytescale.com/docs/cdn" rel="nofollow">CDN</a>
</p>
<hr/>
<br />
## Installation
Install via NPM:
```shell
npm install /upload-widget-vue
```
Or via YARN:
```shell
yarn add /upload-widget-vue
```
Or via a `<script>` tag:
```html
<script src="https://js.bytescale.com/upload-widget-vue/v4"></script>
```
## Usage
### Option 1) File Upload Button โ [Try on CodePen](https://codepen.io/bytescale/pen/yLpvYew?editors=1000)
Create a file upload button using the `openUploadModal` helper:
```html
<template>
<button ="uploadFile">Upload a file...</button>
</template>
<script lang="ts">
import { openUploadModal } from "@bytescale/upload-widget-vue";
import type { UploadWidgetConfig, UploadWidgetResult } from "@bytescale/upload-widget";
import type { PreventableEvent } from "@bytescale/upload-widget-vue";
// Full Configuration:
// https://www.bytescale.com/docs/upload-widget#configuration
const options: UploadWidgetConfig = {
apiKey: "free", // Get API keys from: www.bytescale.com
maxFileCount: 10
};
export default {
name: "App",
methods: {
uploadFile(event: PreventableEvent) {
openUploadModal({
event,
options,
onComplete: (files: UploadWidgetResult[]) => {
if (files.length === 0) {
alert("No files selected.");
} else {
alert(files.map(f => f.fileUrl).join("\n"));
}
}
})
}
}
};
</script>
```
### Option 2) Dropzone โ [Try on CodePen](https://codepen.io/bytescale/pen/RwJvpKG?editors=1000)
Create a file upload dropzone using the `UploadDropzone` component:
```html
<template>
<UploadDropzone :options="options"
:on-complete="onFilesUploaded"
:on-update="onFileListChanged"
width="600px"
height="375px" />
</template>
<script lang="ts">
import { UploadDropzone } from "@bytescale/upload-widget-vue";
import type { UploadWidgetConfig, UploadWidgetResult, UploadWidgetOnUpdateEvent } from "@bytescale/upload-widget";
// Full Configuration:
// https://www.bytescale.com/docs/upload-widget#configuration
const options: UploadWidgetConfig = {
apiKey: "free", // Get API keys from: www.bytescale.com
maxFileCount: 10,
showFinishButton: true
};
export default {
name: "App",
components: {
UploadDropzone
},
data() {
return {
options
};
},
methods: {
onFileListChanged({ uploadedFiles, pendingFiles, failedFiles }: UploadWidgetOnUpdateEvent) {
const uploadedFileUrls = uploadedFiles.map(x => x.fileUrl).join("\n");
console.log(uploadedFileUrls);
},
onFilesUploaded(files: UploadWidgetResult[]) {
if (files.length === 0) {
alert("No files selected.");
} else {
alert(files.map(f => f.fileUrl).join("\n"));
}
}
}
};
</script>
```
> **Special behaviour for dropzones:**
>
> `on-complete` only fires if `showFinishButton = true` (when the user clicks "Finish").
>
> `on-update` must be used when `showFinishButton = false`.
>
> Default value: `showFinishButton = false`
## Result
The callbacks receive a `Array<UploadWidgetResult>`:
```javascript
{
fileUrl: "https://upcdn.io/FW25...", // URL to use when serving this file.
filePath: "/uploads/example.jpg", // File path (we recommend saving this to your database).
accountId: "FW251aX", // Bytescale account the file was uploaded to.
editedFile: undefined, // Edited file (for image crops). Same structure as below.
originalFile: {
fileUrl: "https://upcdn.io/FW25...", // Uploaded file URL.
filePath: "/uploads/example.jpg", // Uploaded file path (relative to your raw file directory).
accountId: "FW251aX", // Bytescale account the file was uploaded to.
originalFileName: "example.jpg", // Original file name from the user's machine.
file: { ... }, // Original DOM file object from the <input> element.
size: 12345, // File size in bytes.
lastModified: 1663410542397, // Epoch timestamp of when the file was uploaded or updated.
mime: "image/jpeg", // File MIME type.
metadata: {
... // User-provided JSON object.
},
tags: [
"tag1", // User-provided & auto-generated tags.
"tag2",
...
]
}
}
```
## โ๏ธ Configuration
All configuration is optional (except for the `apiKey` field, which is required).
```javascript
const options = {
apiKey: "free", // Get API keys from: www.bytescale.com
locale: myCustomLocale, // EN_US by default. (See "Localization" section below.)
maxFileCount: 5, // Unlimited by default (or 1 if multi: false).
maxFileSizeBytes: 1024 ** 2, // Unlimited by default.
mimeTypes: ["image/*"], // Unrestricted by default. Supports * wildcard suffix.
multi: false, // False by default.
onInit: ({ // Exposes lifecycle methods for the component.
close, // Closes the widget when called.
reset, // Resets the widget when called.
updateConfig // Updates the widget's config by passing a new config
}) => {}, // object to the method's first parameter.
onUpdate: (event) => { // Called each time the Upload Widget's list of files change.
// event.pendingFiles // Array of files that are either uploading or queued.
// event.failedFiles // Array of files that failed to upload (due to network or validation reasons).
// event.uploadedFiles // Array of files that have been uploaded and not removed.
},
onPreUpload: async file => ({
errorMessage: "Uh oh!", // Displays this validation error to the user (if set).
transformedFile: file // Uploads 'transformedFile' instead of 'file' (if set).
}),
showFinishButton: true, // Show/hide the "finish" button in the widget.
showRemoveButton: true, // Show/hide the "remove" button next to each file.
styles: {
breakpoints: {
fullScreenWidth: 750, // Full-screen mode activates when the screen is at or below this width.
fullScreenHeight: 420 // Full-screen mode activates when the screen is at or below this height.
},
colors: {
primary: "#377dff", // Primary buttons & links
active: "#528fff", // Primary buttons & links (hover). Inferred if undefined.
error: "#d23f4d", // Error messages
shade100: "#333", // Standard text
shade200: "#7a7a7a", // Secondary button text
shade300: "#999", // Secondary button text (hover)
shade400: "#a5a6a8", // Welcome text
shade500: "#d3d3d3", // Modal close button
shade600: "#dddddd", // Border
shade700: "#f0f0f0", // Progress indicator background
shade800: "#f8f8f8", // File item background
shade900: "#fff" // Various (draggable crop buttons, etc.)
},
fontFamilies: {
base: "arial, sans-serif" // Base font family (comma-delimited).
},
fontSizes: {
base: 16 // Base font size (px).
}
},
path: { // Optional: a string (full file path) or object like so:
fileName: "Example.jpg", // Supports path variables (e.g. {ORIGINAL_FILE_EXT}).
folderPath: "/uploads" // Please refer to docs for all path variables.
},
metadata: {
hello: "world" // Arbitrary JSON metadata (saved against the file).
},
tags: ["profile_picture"], // Requires a Bytescale account.
editor: {
images: {
allowResizeOnMove: true, // True by default. If false, prevents cropper from resizing when moved.
preview: true, // True by default if cropping is enabled. Previews PDFs and videos too.
crop: true, // True by default.
cropFilePath: image => { // Choose the file path used for JSON image crop files.
const {filePath} = image // In: https://www.bytescale.com/docs/types/UploadedFile
return `${filePath}.crop` // Out: https://www.bytescale.com/docs/types/FilePathDefinition
},
cropRatio: 4 / 3, // Width / Height. Undefined enables freeform (default).
cropShape: "rect" // "rect" (default) or "circ".
}
},
}
```
### ๐ณ๏ธ Localization
Default is [EN_US](https://github.com/bytescale/bytescale-upload-widget/blob/main/lib/src/modules/locales/EN_US.ts):
```javascript
const myCustomLocale = {
addAnotherFileBtn: "Add another file...",
addAnotherImageBtn: "Add another image...",
cancelBtn: "cancel",
cancelBtnClicked: "cancelled",
cancelPreviewBtn: "Cancel",
continueBtn: "Continue",
cropBtn: "Crop",
customValidationFailed: "Failed to validate file.",
doneBtn: "Done",
fileSizeLimitPrefix: "File size limit:",
finishBtn: "Finished",
finishBtnIcon: true,
imageCropNumberPrefix: "Image",
maxFilesReachedPrefix: "Maximum number of files:",
maxImagesReachedPrefix: "Maximum number of images:",
orDragDropFile: "...or drag and drop a file.",
orDragDropFileMulti: "...or drag and drop files.",
orDragDropImage: "...or drag and drop an image.",
orDragDropImageMulti: "...or drag and drop images.",
processingFile: "Processing file...",
removeBtn: "remove",
removeBtnClicked: "removed",
submitBtnError: "Error!",
submitBtnLoading: "Please wait...",
unsupportedFileType: "File type not supported.",
uploadFileBtn: "Upload a File",
uploadFileMultiBtn: "Upload Files",
uploadImageBtn: "Upload an Image",
uploadImageMultiBtn: "Upload Images",
xOfY: "of"
}
```
# ๐ API Support
## ๐ File Management APIs
Bytescale provides a wide range of [File Management APIs](https://www.bytescale.com/docs/apis):
- **[File Listing](https://www.bytescale.com/docs/folder-api/ListFolder)**
- **[File Deleting](https://www.bytescale.com/docs/file-api/DeleteFile)**
- **[File Copying](https://www.bytescale.com/docs/file-api/CopyFile)**
- [And more...](https://www.bytescale.com/docs/apis)
## ๐ Media Processing APIs (Image/Video/Audio)
Bytescale also provides real-time [Media Processing APIs](https://www.bytescale.com/docs/media-processing-apis):
- **[Image Processing APIs](https://www.bytescale.com/docs/image-processing-api)** ([resize](https://www.bytescale.com/docs/image-processing-api#image-resizing-api), [crop](https://www.bytescale.com/docs/image-processing-api#image-cropping-api), [convert](https://www.bytescale.com/docs/image-processing-api#f), [compress](https://www.bytescale.com/docs/image-processing-api#image-compression-api) & [watermark](https://www.bytescale.com/docs/image-processing-api#Text-layering-api))
- **[Video Processing APIs](https://www.bytescale.com/docs/video-processing-api)** ([transcode](https://www.bytescale.com/docs/video-processing-api#video-transcoding-api), [optimize](https://www.bytescale.com/docs/video-processing-api#video-compression-api), [resize](https://www.bytescale.com/docs/video-processing-api#video-resizing-api) & [extract metadata](https://www.bytescale.com/docs/video-processing-api#video-metadata-api))
- **[Audio Processing APIs](https://www.bytescale.com/docs/audio-processing-api)** ([transcode](https://www.bytescale.com/docs/audio-processing-api#audio-transcoding-api), [optimize](https://www.bytescale.com/docs/audio-processing-api#audio-compression-api), [trim](https://www.bytescale.com/docs/audio-processing-api#audio-trimming-api) & [extract metadata](https://www.bytescale.com/docs/audio-processing-api#audio-metadata-api))
### Image Processing API (Original Image)
Here's an example using [a photo of Chicago](https://upcdn.io/W142hJk/raw/example/city-landscape.jpg):
<img src="https://upcdn.io/W142hJk/raw/example/city-landscape.jpg" />
```
https://upcdn.io/W142hJk/raw/example/city-landscape.jpg
```
### Image Processing API (Transformed Image)
Using the [Image Processing API](https://www.bytescale.com/docs/image-processing-api), you can produce [this image](https://upcdn.io/W142hJk/image/example/city-landscape.jpg?w=900&h=600&fit=crop&f=webp&q=80&blur=4&text=WATERMARK&layer-opacity=80&blend=overlay&layer-rotate=315&font-size=100&padding=10&font-weight=900&color=ffffff&repeat=true&text=Chicago&gravity=bottom&padding-x=50&padding-bottom=20&font=/example/fonts/Lobster.ttf&color=ffe400):
<img src="https://upcdn.io/W142hJk/image/example/city-landscape.jpg?w=900&h=600&fit=crop&f=webp&q=80&blur=4&text=WATERMARK&layer-opacity=80&blend=overlay&layer-rotate=315&font-size=100&padding=10&font-weight=900&color=ffffff&repeat=true&text=Chicago&gravity=bottom&padding-x=50&padding-bottom=20&font=/example/fonts/Lobster.ttf&color=ffe400" />
```
https://upcdn.io/W142hJk/image/example/city-landscape.jpg
?w=900
&h=600
&fit=crop
&f=webp
&q=80
&blur=4
&text=WATERMARK
&layer-opacity=80
&blend=overlay
&layer-rotate=315
&font-size=100
&padding=10
&font-weight=900
&color=ffffff
&repeat=true
&text=Chicago
&gravity=bottom
&padding-x=50
&padding-bottom=20
&font=/example/fonts/Lobster.ttf
&color=ffe400
```
## Authentication
Bytescale supports two types of authentication:
### API Keys
The Bytescale Upload Widget uses the `apiKey` parameter to authenticate with [Bytescale](https://www.bytescale.com/).
With API key auth, the requester has access to the resources available to the API key:
- Secret API keys (`secret_***`) can perform all API operations (see: [Bytescale JavaScript SDK](https://www.bytescale.com/docs/sdks/javascript)).
- Public API keys (`public_***`) can perform file uploads and file downloads only. File overwrites, file deletes, and all other destructive operations cannot be performed using public API keys.
You must always use **public API keys** (e.g. `public_***`) in your client-side code.
Each API key can have its read/write access limited to a subset of files/folders.
### JWTs
JWTs are optional.
With JWTs, users can download private files directly via the URL, as authentication is performed implicitly via a session cookie _or_ via an `authorization` header if service workers are enabled (see the `serviceWorkerScript` param on the `AuthManager.beginAuthSession` method). This allows the browser to display private files in `<img>`, `<video>`, and other elements.
With JWTs, users can upload files to per-user folders. This is because the permissions in the [JWT's payload](https://www.bytescale.com/docs/types/BytescaleJwt) can be generated at runtime. The [Bytescale Upload Widget](https://www.bytescale.com/docs/upload-widget) internally uses the [Bytescale JavaScript SDK](https://www.bytescale.com/docs/sdks/javascript) to perform file uploads: the Bytescale JavaScript SDK handles the JWT refresh process with your API, requesting new JWTs when required, and includes your JWTs in all subsequent requests to the Bytescale API.
[Learn more about the `AuthManager` and JWTs ยป](https://www.bytescale.com/docs/auth)
## UrlBuilder
The [Bytescale JavaScript SDK](https://github.com/bytescale/bytescale-javascript-sdk) exports a `UrlBuilder` to construct URLs from `filePaths`:
```javascript
import { UrlBuilder } from "@bytescale/sdk";
```
**Recommended:** you should always save `filePaths` to your DB instead of `fileUrls`.
#### Raw Files
To get the URL for the uploaded image `/example.jpg` in its original form, use the following:
```javascript
// Returns: "https://upcdn.io/1234abc/raw/example.jpg"
UrlBuilder.url({
accountId: "1234abc",
filePath: "/example.jpg"
});
```
#### Images
To resize the uploaded image `/example.jpg` to 800x600, use the following:
```javascript
// Returns: "https://upcdn.io/1234abc/image/example.jpg?w=800&h=600"
UrlBuilder.url({
accountId: "1234abc",
filePath: "/example.jpg",
options: {
transformation: "image",
transformationParams: {
w: 800,
h: 600
}
}
});
```
[Image Processing API Docs ยป](https://www.bytescale.com/docs/image-processing-api)
#### Videos
To transcode the uploaded video `/example.mov` to MP4/H.264 in HD, use the following:
```javascript
// Returns: "https://upcdn.io/1234abc/video/example.mov?f=mp4-h264&h=1080"
UrlBuilder.url({
accountId: "1234abc",
filePath: "/example.mov",
options: {
transformation: "video",
transformationParams: {
f: "mp4-h264",
h: 1080
}
}
});
```
[Video Processing API Docs ยป](https://www.bytescale.com/docs/video-processing-api)
#### Audio
To transcode the uploaded audio `/example.wav` to AAC in 192kbps, use the following:
```javascript
// Returns: "https://upcdn.io/1234abc/audio/example.wav?f=aac&br=192"
UrlBuilder.url({
accountId: "1234abc",
filePath: "/example.wav",
options: {
transformation: "audio",
transformationParams: {
f: "aac",
br: 192
}
}
});
```
[Audio Processing API Docs ยป](https://www.bytescale.com/docs/audio-processing-api)
#### Archives
To extract the file `document.docx` from the uploaded ZIP file `/example.zip`:
```javascript
// Returns: "https://upcdn.io/1234abc/archive/example.zip?m=extract&artifact=/document.docx"
UrlBuilder.url({
accountId: "1234abc",
filePath: "/example.zip",
options: {
transformation: "archive",
transformationParams: {
m: "extract"
},
artifact: "/document.docx"
}
});
```
[Archive Processing API Docs ยป](https://www.bytescale.com/docs/archive-processing-api)
## ๐ Can I use my own storage?
Bytescale supports AWS S3, Cloudflare R2, Google Storage, DigitalOcean, and Bytescale Storage.
**[Bytescale Storage Docs ยป](https://www.bytescale.com/docs/storage/sources)**
## ๐ Create your Bytescale Account
Bytescale is the best way to upload, transform, and serve images, videos, and audio at scale.
**[Create a Bytescale account ยป](https://www.bytescale.com/get-started)**
#### Full Documentation
- **[Bytescale Upload Widget Docs ยป](https://www.bytescale.com/docs/upload-widget)**
- **[Bytescale Upload Widget (React) Docs ยป](https://www.bytescale.com/docs/upload-widget/frameworks/react)**
- **[Bytescale Upload Widget (Angular) Docs ยป](https://www.bytescale.com/docs/upload-widget/frameworks/angular)**
- **[Bytescale Upload Widget (Vue.js) Docs ยป](https://www.bytescale.com/docs/upload-widget/frameworks/vue)**
- **[Bytescale Upload Widget (jQuery) Docs ยป](https://www.bytescale.com/docs/upload-widget/frameworks/jquery)**
## License
[MIT](LICENSE)