npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

rxjs-uploader

v1.6.1

Published

A simple RxJS-powered interface for uploading files.

Downloads

178

Readme

RxJS Uploader

A simple RxJS-powered interface for uploading files.

Demo: https://lsqlabs.github.io/rxjs-uploader

Stackblitz: https://stackblitz.com/edit/rxjs-uploader-example

Installation

npm install rxjs-uploader

Publishing Updates to npm

  1. Make sure you are logged in via cli to the npm account that owns the package (if you work at LSQ Funding, check the 1Password vault).
  2. Increment the version number in projects/rxjs-uploader/package.json appropriately.
  3. npm run build:lib
  4. npm run publish

Basic Usage

import { Uploader } from 'rxjs-uploader';

// You may pass configuration options to the Uploader constructor.
const uploader = new Uploader({
    allowedContentTypes: [ 'application/pdf' ]
});

// Or you can set them using setter methods.
uploader.setRequestUrl(
    'https://www.mocky.io/v2/5185415ba171ea3a00704eed'
);

// Once configured, call `streamFileUploads` to ready the uploader.
// The method returns the stream of `FileUpload`s.
const fileUploads$ = uploader.streamFileUploads();

// Calling `streamFileUploads` without arguments tells Uploader to use
// the default file source, a hidden <input type="file"> appended to
// <body> when `Uploader` instantiates. `selectFiles` fires a `click`
// event on that input.
uploader.selectFiles();

Simple example

<!-- Single file input -->
<input id="file-source" type="file">
<!-- OR multiple file input -->
<input id="file-source" type="file" multiple>
<!-- OR drop zone -->
<div id="file-source"></div>
const fileUploads$ = new Uploader()
    .setRequestUrl('https://www.mocky.io/v2/5185415ba171ea3a00704eed')
    .streamFileUploads(document.getElementById('file-source'));

// Alternatively, let Uploader use its default <input> (which is
// hidden and appended to <body>).
// Use it by calling `uploader.selectFiles()`.
// By default, the file input is of type 'single'. You can pass the
// type you want ('single' or 'multiple') to streamFileUploads.

const uploader = new Uploader({
    requestUrl: 'https://www.mocky.io/v2/5185415ba171ea3a00704eed'
});
const fileUploads$ = uploader.streamFileUploads('multiple');
uploader.selectFiles();

Advanced example (using Angular)

@Component({
    selector: 'uploader-demo',
    template: `
        <button (click)="hiddenFileInput.click()">
            I'm a prettier alternative to the file input
        </button>
        <button (click)="uploader.clear()">Cancel all</button>
    `
})
export class UploaderDemoComponent implements AfterViewInit {
    public fileUploads$: Observable<FileUpload[]>;
    public hiddenFileInput = Uploader.createFileInputElement(
        'multiple'
    );
    public uploader = new Uploader({
        allowedContentTypes: [
            'image/jpeg',
            'image/png',
            'application/pdf'
        ],
        fileCountLimit: 100,
        fileSizeLimitMb: 10,
        uploadFileAsBody: false, // Default is false. When true, the file will be the body of the request, instead of being sent as form data.
        onFileCountLimitExceeded: (fileCountLimit) => alert(
            'You attempted to upload more than the limit of '
            + fileCountLimit + ' files'
        ),
        requestOptionsFactory: (fileUpload) => {
            return {
                url: 'https://api.myawesomeservice.com/upload/'
                    + fileUpload.name,
                headers: {
                    'content-length': `${fileUpload.file.size}`
                },
                withCredentials: false // Default is false. When true, the XHR withCredentials flag will be set to allow propagating cookies.
            };
        },
        allFilesQueuedCallback: (fileUploads) => {
            // It's possible you'll want to do some async stuff
            // before actually executing the upload. To do that,
            // simply return a Promise.
            return new Promise((resolve, reject) => {
                // Simulating an HTTP call.
                setTimeout(() => {
                    console.log(
                        fileUploads.length
                        + ' files are ready to upload'
                    );
                    resolve(fileUploads);
                }, 1000);
            });
        },
        fileUploadedCallback: (fileUpload) => {
            console.log(fileUpload.name + ' was uploaded');
            return fileUpload;
        },
        allFilesUploadedCallback: (fileUploads) => {
            console.log(fileUploads.length + ' files were uploaded');
        },
        disallowedContentTypeErrorMessage: (file) => {
            return `${file.name} is an unsupported file type: ${file.type}`;
        },
        disallowedContentSizeErrorMessage: (file) => {
            return `${file.name} is larger than the limit of 100mb.`;
        },
    })

    public ngAfterViewInit(): void {
        this.fileUploads$ = this.uploader
            .streamFileUploads(this.hiddenFileInput);
    }
}

API

class Uploader<FileUploadType extends FileUpload> {
    /**
     * A stream indicating whether the user is currently dragging
     * files over any registered drop zone.
     */
    isDraggedOverStream: Observable<boolean>;
    /**
     * A stream of any errors that occur during upload.
     */
    errorStream: Observable<UploaderError>;
    /**
     * Return an `HTMLInputElement` with the specified `accept`, and
     * `className` attributes.
     * If called with no arguments, creates a single-file `input`.
     */
    static createFileInputElement(
        type?: 'single' | 'multiple',
        accept?: string,
        className?: string
    ): HTMLInputElement;

    constructor(
        config?: IUploaderConfig<FileUploadType> | 'single' | 'multiple'
    );

    /**
     * Take one or more `FileSource`s (`input[type="file"]`s or drop
     * zone target elements) and return an observable of
     * `FileUpload`s, executing the uploads immediately (if an
     * `allFilesQueuedCallback` does not exist) or when the
     * `allFilesQueuedCallback` returns or resolves.
     */
    streamFileUploads(
        ...fileSources: FileSource[]
    ): Observable<FileUploadType[]>;
    /**
     * Pipe an empty array to the stream returned by
     * `streamFileUploads`, unsubscribe from all open subscriptions,
     * and set all associated file input elements' `value` property
     * to `''`.
     */
    clear(): void;
    /**
     * Call `.click()` on the default file input element (created and
     * appended to <body> when Uploader is instantiated). Useful when
     * calling `streamFileUploads` with no arguments.
     */
    selectFiles(): void;
    setRequestUrl(url: string): this;
    setRequestOptions(
        factoryOrOptions?: IUploadRequestOptions
            | ((fileUpload?: FileUploadType) => Promise<IUploadRequestOptions>)
    ): this;
    setRequestOptionsFactory(
        factory: (fileUpload?: FileUploadType) =>
            Promise<IUploadRequestOptions>
    ): this;
    patchRequestOptions(
        requestOptionsPatch?: Partial<IUploadRequestOptions>
    ): this;
}
interface IFileUpload {
    /** The state and percentage of the file's upload progress. */
    progress: IProgress;
    /** The response, if any, returned from the HTTP upload call. */
    response: Response;
    /** The code from the HTTP response, if any (e.g. `200`). */
    responseCode: number;
    /** The HTTP response body, if any. */
    responseBody: any;
    /** Set to `true` the first time the file upload is executed. */
    uploadHasStarted: boolean;
    /**
     * Receives a value every time the FileUpload is supposed to
     * execute its HTTP upload.
     */
    executeStream: Observable<void>;
    /**
     * Receives `false` when `markForRemoval()` is called, telling
     * `Uploader` to delete it from memory and cancel the upload if
     * one is pending.
     */
    isMarkedForRemovalStream: Observable<boolean>;

    /** Returns the value passed to `setRequestOptions()`. */
    readonly requestOptions: IUploadRequestOptions;
    /** A unique identifier for the `FileUpload`. */
    readonly id: Symbol;
    /** The `name` taken from `file.name`. */
    readonly name: string;
    /** Percentage of the upload that has been completed. */
    readonly progressPercentage: number;
    /** Boolean indicating whether an upload is in progress. */
    readonly uploading: boolean;
    /**
     * Boolean indicating whether the upload has completed, either
     * successfully or not.
     */
    readonly uploaded: boolean;
    /** Boolean indicating whether the upload has succeeded. */
    readonly succeeded: boolean;
    /** Boolean indicating whether the upload has failed. */
    readonly failed: boolean;
    /**
     * Boolean indicating whether the upload has been marked as a
     * failure by `Uploader`.
     */
    readonly rejected: boolean;
    /**
     * Boolean indicating whether the `FileUpload` has been marked
     * for deletion by `Uploader`.
     */
    readonly isMarkedForRemoval: boolean;

    /** Used by `Uploader` to mark a `FileUpload` as failed. */
    reject(errorResponse?: any): void;
    /**
     * Set the `IUploadRequestOptions`, used to construct the HTTP
     * request.
     */
    setRequestOptions(options: IUploadRequestOptions): void;
    /** Used by `Uploader` to construct the HTTP request. */
    createRequest(): {
        method: HttpMethod,
        url: string,
        body: FormData,
        headers?: { [key: string]: string }
    };
    /** Resets the state of the `FileUpload`. */
    reset(): void;
    /** Executes the file upload. */
    retry(): void;
    /** Used by `Uploader` to mark the `FileUpload` for deletion. */
    markForRemoval(): void;
    /** Alias for {@link IFileUpload#markForRemoval}` */
    remove(): void;
}

type FileUploadCallbackReturn<ReturnType> =
    Promise<ReturnType> | ReturnType | void;

interface IUploaderConfig<FileUploadType
        extends FileUpload = FileUpload> {
    allowedContentTypes?: string[];
    fileCountLimit?: number | (() => number);
    fileSizeLimitMb?: number;
    dragAndDropFlagSelector?: string;
    requestUrl?: string;
    requestOptions?: ((fileUpload?: FileUploadType) =>
        Promise<IUploadRequestOptions>) | IUploadRequestOptions;
    fileUploadType?: any;
    allFilesQueuedCallback?: (
        fileUploads: FileUploadType[]
    ) => FileUploadCallbackReturn<FileUploadType[]>;
    fileUploadedCallback?: (
        fileUpload: FileUploadType
    ) => FileUploadCallbackReturn<FileUploadType>;
    allFilesUploadedCallback?: (
        fileUploads: FileUploadType[]
    ) => FileUploadCallbackReturn<FileUploadType[]>;
    onFileCountLimitExceeded?: (fileCountLimit: number) => void;
}

interface IProgress {
    percent: number;
    state: ProgressState;
}

interface IUploadRequestOptions {
    url: string;
    method?: HttpMethod;
    formData?: FormData;
    headers?: { [key: string]: string };
    withCredentials?: boolean;
}

enum ProgressState {
    NotStarted,
    Idle,
    InProgress,
    Completed,
    Failed,
    Cancelled
}