@algonauti/ember-active-storage

中文文档
3.0.0发布于 2026/10/17MITTypeScriptCommonJS近 30 天下载 3817

Power your ember.js application with activestorage

原始使用文档
文档目录14 节
  1. Installation
  2. Usage
  3. Events
  4. XHR object
  5. Metadata
  6. Configuration
  7. Sending authentication headers
  8. TypeScript
  9. Contributing
  10. Installation
  11. Linting
  12. Running tests
  13. Running the test app
  14. License

ember-active-storage

CI Ember Observer Score

Installation

ember install @algonauti/ember-active-storage

Usage

The addon provides an activeStorage service that allows you to:

  • send files to your Rails backend's direct upload controller;
  • listen to upload progress events.

Assuming your template has a file input like:

<input type="file" {{on "change" (fn this.upload)}} />

and your ember model has an avatar attribute defined as has_one_attached :avatar on its corresponding Active Record model, then in your component (or controller) the upload action would look like:

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { service } from '@ember/service';

export default class UploadComponent extends Component {
  @service
  activeStorage;

  @tracked
  uploadProgress = 0;

  @action
  upload(event) {
    const files = event.target.files;
    if (files) {
      const directUploadURL = '/rails/active_storage/direct_uploads';

      for (var i = 0; i < files.length; i++) {
        this.activeStorage
          .upload(files.item(i), directUploadURL, {
            onProgress: (progress, event) => {
              this.uploadProgress = progress;
            },
          })
          .then((blob) => {
            const signedId = blob.signedId;

            this.model.avatar = signedId;
          });
      }
    }
  }
}
  • directUploadURL is the path referencing ActiveStorage::DirectUploadsController on your Rails backend (or a custom one built on top of that).
  • The uploadProgress property will hold a value between 0 and 100 that you might use in your template to show upload progress.
  • After the upload promise is resolved and signedId is set in your model, when a model.save() is triggered, the Rails backend will use such signedId to associate an ActiveStorage::Attachment record to your backend model's record.

Events

loadstart, load, loadend, error, abort, timeout events invokes onLoadstart, onLoad, onLoadend, onError, onAbort, onTimeout accordingly. For example; If you want to use the loadend event in your app, you can use like;

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { service } from '@ember/service';

export default class UploadComponent extends Component {
  @service
  activeStorage;

  @tracked
  uploadProgress = 0;

  @action
  upload(event) {
    const files = event.target.files;
    if (files) {
      const directUploadURL = '/rails/active_storage/direct_uploads';

      for (var i = 0; i < files.length; i++) {
        this.activeStorage
          .upload(files.item(i), directUploadURL, {
            onProgress: (progress, event) => {
              this.uploadProgress = progress;
            },
            onLoadend: (event) => {
              debug(`Event captured ${event}`); // https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent
            },
          })
          .then((blob) => {
            const signedId = blob.signedId;

            this.model.avatar = signedId;
          });
      }
    }
  }
}

XHR object

If you need the actual XHR object in your app, you can use the onXHROpened event. It returns the XHR object reference. For example:

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { service } from '@ember/service';

export default class UploadComponent extends Component {
  @service
  activeStorage;

  @tracked
  uploadProgress = 0;

  @tracked
  xhrs = [];

  @action
  upload(event) {
    const files = event.target.files;
    if (files) {
      const directUploadURL = '/rails/active_storage/direct_uploads';

      for (var i = 0; i < files.length; i++) {
        this.activeStorage
          .upload(files.item(i), directUploadURL, {
            onProgress: (progress, event) => {
              this.uploadProgress = progress;
            },
            onXHROpened: (xhr) => {
              this.xhrs.push(xhr);  // so you can loop over this.xhrs and invoke abort()
            },
          })
          .then((blob) => {
            const signedId = blob.signedId;

            this.model.avatar = signedId;
          });
      }
    }
  }
}

Metadata

ActiveStorage supports metadata for direct uploads. That is a nice way to provide extra information to the rails app.

class DirectUploadsController < ActiveStorage::DirectUploadsController
  def create
    # blob_args[:metadata]['additional_type']
    # => my_type
    blob = ActiveStorage::Blob.create_before_direct_upload!(**blob_args)
    render json: direct_upload_json(blob)
  end
end
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { service } from '@ember/service';

export default class UploadComponent extends Component {
  @service
  activeStorage;

  @action
  upload(event) {
    const files = event.target.files;
    if (files) {
      const directUploadURL = '/rails/active_storage/direct_uploads';

      for (var i = 0; i < files.length; i++) {
        this.activeStorage
          .upload(files.item(i), directUploadURL, {
            metadata: {
              additional_type: 'my_type'
            },
          })
          .then((blob) => {
            const signedId = blob.signedId;

            this.model.avatar = signedId;
          });
      }
    }
  }
}

Configuration

There is an ember-active-storage ENV config with only one parameter called url. With this config help, you can omit the upload url now. For example:

ENV['ember-active-storage'] = {
  url: 'http://your-domain/rails/active_storage/direct_uploads',
};

Now you can call the upload function without the upload url.

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { service } from '@ember/service';

export default class UploadComponent extends Component {
  @service
  activeStorage;

  @tracked
  uploadProgress = 0;

  @action
  upload(event) {
    const files = event.target.files;
    if (files) {
      for (var i = 0; i < files.length; i++) {
        this.activeStorage
          .upload(files.item(i), {
            onProgress: (progress, event) => {
              this.uploadProgress = progress;
            },
          })
          .then((blob) => {
            const signedId = blob.signedId;

            this.model.avatar = signedId;
          });
      }
    }
  }
}

Sending authentication headers

It's pretty common that you want to protect with authentication the direct uploads endpoint on your Rails backend. If that's the case, the activeStorage service will need to send authentication headers together with the direct upload request.

To achieve that, you'll need to extend the activeStorage service provided by the addon and add a headers computed property. For example, if you're using ember-simple-auth, it will be a 2-steps process. First you'll need to define an authenticatedHeaders computed property in your session service, like this:

// app/services/session.js
import Service from '@ember/service';
import { service } from '@ember/service';

export default class MySessionService extends Service {
  @service
  session;

  get authenticatedHeaders() {
    const { access_token } = this.session.authenticated;

    return { Authorization: `Bearer ${access_token}` };
  }
}

Then, you will alias that property in your activeStorage service, like this:

// app/services/active-storage.js
import ActiveStorage from '@algonauti/ember-active-storage/services/active-storage';
import { service } from '@ember/service';

export default class ActiveStorageService extends ActiveStorage {
  @service('my-session')
  session;

  get headers() {
    this.session.authenticatedHeaders;
  }
}

Also note: if the download endpoint is protected as well, and you're using an ajax request to download files, then don't forget to include the same headers in that request as well.

TypeScript

The addon is written in TypeScript and ships its own type declarations.

import Component from '@glimmer/component';
import { service } from '@ember/service';
import type { ActiveStorageService, UploadOptions } from '@algonauti/ember-active-storage';

export default class UploadComponent extends Component {
  @service declare activeStorage: ActiveStorageService;

  upload = async (event: Event) => {
    const file = (event.target as HTMLInputElement).files?.[0];

    if (file) {
      const options: UploadOptions = {
        onProgress: (progress) => console.log(progress),
      };
      const blob = await this.activeStorage.upload(file, '/rails/active_storage/direct_uploads', options);

      console.log(blob.signedId);
    }
  };
}

Exported types: ActiveStorageConfig, DirectUploadData, DirectUploadResponse, Headers, RequestOptions, UploadEventHandlers, UploadOptions, UploadResponse and UploadResponseOptions. ActiveStorageService, Blob, FileChecksum, Uploader and the uploadResponse test helper are exported as well.

Contributing

Installation

  • git clone <repository-url>
  • cd ember-active-storage
  • pnpm install

Linting

  • pnpm lint
  • pnpm lint:fix

Running tests

  • pnpm test – Runs the test suite on the current Ember version
  • cd test-app && ember try:each – Runs the test suite against multiple Ember versions

Running the test app

For more information on using ember-cli, visit https://ember-cli.com/.

License

This project is licensed under the MIT License.

版本与发布记录

@algonauti/ember-active-storage 共发布 10 个版本,首次发布于 2021/03/23;过去 12 个月发布了 1 个版本。

版本发布日期距今
3.0.0latest2026/10/1722 天前
2.3.02023/06/063 年前
2.2.12023/06/063 年前
2.2.02022/08/274 年前
2.1.12021/12/024 年前
2.0.32021/09/225 年前

关于 @algonauti/ember-active-storage 的常见问题

@algonauti/ember-active-storage 是什么?

Power your ember.js application with activestorage

如何安装 @algonauti/ember-active-storage?

在项目目录运行 npm install @algonauti/ember-active-storage,或使用 pnpm add / yarn add / bun add。

@algonauti/ember-active-storage 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@algonauti/ember-active-storage 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@algonauti/ember-active-storage 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >= 20.19。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@algonauti/ember-active-storage 可以商用吗?

@algonauti/ember-active-storage 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@algonauti/ember-active-storage 的下载量有多少?

根据 npm 官方下载统计,@algonauti/ember-active-storage 在 2026/10/06 – 2026/11/04 这 30 天内被下载 3,817 次,日均约 127 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@algonauti/ember-active-storage 还在维护吗?

最新版本 3.0.0 发布于 2026/10/17(22 天前);至今共发布 10 个版本,其中过去 12 个月发布了 1 个;首次发布于 2021/03/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。