# @winman-f2e/nos-js-web

> 浏览器上使用的nos直传

Latest version **1.0.1** (published 2024-01-23) · MIT license · 0 weekly downloads

## Install

```sh
npm install @winman-f2e/nos-js-web
pnpm add @winman-f2e/nos-js-web
yarn add @winman-f2e/nos-js-web
bun add @winman-f2e/nos-js-web
```

## Health

**Score 20/100 (F)** — status: abandoned.

Positive: has types; no vulnerabilities.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.1 |
| Published | 2024-01-23 |
| First published | 2021-09-13 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 1 |
| Unpacked size | 30.2 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Maintainers | luofeiyue, whiskeyi, yousy, guoyeeg, azgaga, liushichuan, yaochen, shenyuan, aflyermin, ignous, jeekdong |

## Links

- npm: https://www.npmjs.com/package/@winman-f2e/nos-js-web
- npm.io page: https://npm.io/package/@winman-f2e/nos-js-web

## Dependencies (1)

- [@winman-f2e/nos-js](https://npm.io/package/@winman-f2e/nos-js.md) ~1.0.2

## Recent versions

- 1.0.1 (latest) — 2024-01-23
- 1.0.1-beta.0 (beta) — 2024-01-17
- 1.0.0 — 2023-09-12
- 1.0.0-beta.3 — 2022-12-13
- 1.0.0-beta.2 — 2022-12-13
- 1.0.0-beta.1 — 2022-12-13
- 1.0.0-beta.0 — 2022-12-08
- 0.1.6 — 2022-09-01
- 0.1.6-beta.3 — 2022-08-23
- 0.1.6-beta.2 — 2022-08-17
- 0.1.6-beta.1 — 2022-08-15
- 0.1.6-beta.0 — 2022-06-27
- 0.1.5 — 2022-04-14
- 0.1.4 — 2021-12-07
- 0.1.3 — 2021-11-17
- … 3 more at https://npm.io/package/@winman-f2e/nos-js-web/versions

## README

# @winman-f2e/nos-js-web
浏览器上使用的nos直传

- [@winman-f2e/nos-js-web](#winman-f2enos-js-web)
  - [安装](#安装)
  - [快速开始](#快速开始)
    - [第一步 创建一个通用上传配置](#第一步-创建一个通用上传配置)
    - [第二步 开始上传](#第二步-开始上传)
  - [常见问题](#常见问题)
  - [写在前面的几个概念](#写在前面的几个概念)
    - [NOSClass 平台适配类](#nosclass-平台适配类)
    - [Uploader 上传行为](#uploader-上传行为)
    - [upload 上传实例](#upload-上传实例)
    - [task 一个具体的文件上传任务](#task-一个具体的文件上传任务)
      - [task的属性](#task的属性)
        - [task.uploadInfo 上传中进度信息](#taskuploadinfo-上传中进度信息)
        - [task.uploadedInfo 上传完成后的nos数据](#taskuploadedinfo-上传完成后的nos数据)
      - [task的函数](#task的函数)
        - [task.upload()](#taskupload)
        - [task.pause()](#taskpause)
        - [task.abort()](#taskabort)
        - [task.destroy()](#taskdestroy)
        - [task.addListener|task.removeListener](#taskaddlistenertaskremovelistener)
      - [task的事件](#task的事件)
        - [progress](#progress)
        - [complete](#complete)
        - [error](#error)
  - [更多使用方法](#更多使用方法)
    - [定义自己的业务 NOS token 获取方式](#定义自己的业务-nos-token-获取方式)
      - [1. 实例化后注入](#1-实例化后注入)
      - [2. 创建新类](#2-创建新类)
    - [使用非nos.netease.com上传，如建德的NOS分区](#使用非nosneteasecom上传如建德的nos分区)
    - [配置并行上传的分块大小](#配置并行上传的分块大小)
    - [自定义上传逻辑](#自定义上传逻辑)
  - [API](#api)
    - [来自 @winman-f2e/nos-js](#来自-winman-f2enos-js)
    - [特有API](#特有api)
      - [WebAdapter 和 NOSWebClass](#webadapter-和-noswebclass)
      - [NOSWeb](#nosweb)
      - [BlockNOSWeb](#blocknosweb)
      - [ConcurrentNOSWeb](#concurrentnosweb)
  - [RichMediaURL 富媒体url处理工具](#richmediaurl-富媒体url处理工具)

## 安装
```shell
npm i @winman-f2e/nos-js-web
```

## 快速开始
### 第一步 创建一个通用上传配置
比如`shared/nos-uploader`
```ts
import {
  NOSWebClass,
  NOSDefaultUploader
} from '@winman-f2e/nos-js-web';

const NOSUploader = new NOSWebClass(NOSDefaultUploader);
// NOSDefaultUploader 是使用的上传方式
// 有以下3种方式可供选择
// - NOSDefaultUploader - 默认上传方式，单请求上传整个文件
// - NOSBlockUploader - NOS分块直传，串行上传
// - NOSConcurrentUploader - 并行分块直传

NOSUploader.useGetToken((uploader) => {
  const file = uploader._originFile;
  return fetch(
    // 这是业务接口的内容，可以查看nos直传文档（https://sf.163.com/help/documents/67031136644288512）了解接口内容
    '/genUploadToken'
  ).then(({ response }) => {
      const data = await response.json();
      return {
        objectKey: data.objectName, // NOS 对象名
        bucketName: data.bucketName, // NOS 桶名
        token: data.uploadToken // NOS 直传凭证： https://sf.163.com/help/documents/67031136644288512
      }
    })
})

export NOSUploader;
```

### 第二步 开始上传
比如在react中使用
```tsx
import React, {
  useCallback
} from 'react'
import { NOSUploader } from './shared/nos-uploader';

const Uploader = () => {
  const uploadFile = useCallback((file) => {
    const task = NOSUploader.createUploadTask(file);
    // 监听上传进度
    task.addListener('progress', () => {
      // 上传中信息，详细内容可查看【#task.uploadInfo 上传中进度信息】
      /**
      {
        percent: number,
        timestamp: number,
        sent?: number,
        total?: number
      }
      **/
      task.uploadInfo
    );
    // 监听上传完成
    task.addListener('complete', () => {
      task.uploadedInfo
    });
    // 监听上传错误
    task.addListener('error', () => {});

    // 开始上传
    task.upload();
  }, []);
  return (
    <div>
      <button
        type="file"
        onClick={(event) => {
          uploadFile(event.target.files[0])
        }}
      ></button>
    </div>
  )
}
```

总共2步，第二步是可以在任意业务场景中使用的。

## 常见问题
[FAQ](./docs/faq.md)

## 写在前面的几个概念
### NOSClass 平台适配类
- NOSClass
- NOSWebClass

### Uploader 上传行为
- NOSDefaultUploader - 默认上传方式，单请求上传整个文件
- NOSBlockUploader - NOS分块直传，串行上传
- NOSConcurrentUploader - 并行分块直传

### upload 上传实例
```ts
const upload = new NOSWebClass(
  NOSConcurrentUploader // NOSDefaultUploader或NOSBlockUploader或NOSConcurrentUploader
)
```
这句表示，用**浏览器支持的方式构建一个并行分块上传**

### task 一个具体的文件上传任务
```ts
const task = upload.createUploadTask(file)
```

#### task的属性

##### task.uploadInfo 上传中进度信息
```ts
export interface UploadInfo {
  /**
   * 上传进度 0-1
   */
  percent: number,
  /**
   * 上次更新的时间戳
   */
  timestamp: number,
  /**
   * 已发送的文件大小
   */
  sent?: number,
  /**
   * 文件总大小
   */
  total?: number
}
```

##### task.uploadedInfo 上传完成后的nos数据
```ts
/**
 * UploadedInfo 上传完成后的nos数据
 */
export interface UploadedInfo {
  objectKey: string,
  bucketName: string
}
```

#### task的函数
##### task.upload()
开始上传

##### task.pause()
暂停上传，暂停后调用task.upload会继续上传

##### task.abort()
取消上传，进度清零

##### task.destroy()
销毁，会取消上传，并移除所有事件

##### task.addListener|task.removeListener
添加和移除事件，可以查看[task的事件](#task的事件)

#### task的事件
可以通过
```ts
const callback = () => {};
// 添加监听事件
task.addListener('事件名字', callback);
// 移除监听事件
task.removeListener('事件名字', callback);

// 移除所有该事件名上的监听
task.removeListener('事件名字');
// 移除该函数绑定的所有事件
task.removeListener(callback);
```
##### progress
上传进度事件

##### complete
上传完成事件

##### error
上传错误事件
```ts
import {
  ERROR_CODE_MAP
} from '@winman-f2e/nos-js-web';

task.addListener('error', (event) => {
  const error = event.data;
  if (error.code === ERROR_CODE_MAP.CONCURRENT_GET_BLOCK_MD5_FAIL.code) {
    // concurrentUploader获取分片md5失败
  }
});
```
ERROR_CODE_MAP说明：

|key|错误说明|code|message|
|---|---|---|---|
|GET_TOKEN_FAIL|获取token失败|100|get NOS token fail|
|DEFAULT_UPLOAD_FAIL|defaultUploader上传失败|1000||
|BLOCK_RETRIES_EXCEEDED|blockUploader重试过多，上传失败|2000|upload error: retries have exceeded the limit.|
|BLOCK_FETCH_OFFSET_FAIL|blockUploader获取上次上传进度失败|2001||
|CONCURRENT_GET_UPLOAD_ID_FAIL|concurrentUploader获取uploadId失败|3000||
|CONCURRENT_GET_BLOCK_MD5_FAIL|concurrentUploader获取分片md5失败|3001||
|CONCURRENT_GET_COMPLETE_ALL_BLOCKS_FAIL|concurrentUploader分片完成后，合并分片失败|3002||
|CONCURRENT_RETRIES_EXCEEDED|concurrentUploader重试次数过多|3003||


## 更多使用方法

### 定义自己的业务 NOS token 获取方式

#### 1. 实例化后注入
```ts
import {
  NOSWebClass,
  NOSDefaultUploader
} from '@winman-f2e/nos-js-web';

const upload = new NOSWebClass(NOSDefaultUploader);

upload.useGetToken((uploader) => {
  const file = uploader._originFile;
  return fetch(
    '/genUploadToken'
  ).then(({ response }) => {
      const data = await response.json();
      return {
        objectKey: data.objectName, // NOS 对象名
        bucketName: data.bucketName, // NOS 桶名
        token: data.uploadToken // NOS 直传凭证： https://sf.163.com/help/documents/67031136644288512
      }
    })
})
```

#### 2. 创建新类
```ts
class CustomNOSClass extends NOSWebClass {
  getNOSToken: (uploader) => Promise<{ objectKey: string, bucketName: string, token: string}>
}
```

当然也可以在实例化后在此之前动态修改
```ts
const customNOS = new CustomNOSClass(NOSDefaultUploader);

customNOS.useGetToken(() => {
  // ....
})
```

### 使用非nos.netease.com上传，如建德的NOS分区
如果我们申请的NOS桶分区不属于 nos.netease.com。如果属于建德的，那我们可以修改如下：
```ts
import {
  APIS
} from '@winman-f2e/nos-js';
import {
  NOSWebClass,
  NOSDefaultUploader
} from '@winman-f2e/nos-js-web';

const upload = new NOSWebClass(NOSDefaultUploader);

upload.updateAPI(APIS.jd);
```

当然也可以根据实际的需求进行变更。变更方法如下：
```ts
upload.updateAPI({
  NOS_UPLOAD: {
    url: 'https://{bucketName}.nos-jd.163yun.com',
    method: 'POST'
  },

  NOS_GET_OBJECT: {
    url: 'https://{bucketName}.nos-jd.163yun.com/{objectKey}',
  },

  NOS_BLOCK_UPLOAD: {
    url: 'https://{bucketName}.nos-jd.163yun.com/{objectKey}',
    method: 'POST'
  },

  // 获取上传进度
  FETCH_BLOCK_UPLOAD_CONTEXT: {
    url: 'https://{bucketName}.nos-jd.163yun.com{objectKey}',
    method: 'GET'
  },

  NOS_CONCURRENT_UPLOAD: {
    url: 'https://{bucketName}.nos-jd.163yun.com/{objectKey}'
  }
})
```

其中 `{bucketName}`和`{objectKey}` 是 `useGetToken` 返回数据的占位符，会自动替换

### 配置并行上传的分块大小
```ts
import {
  NOSWebClass,
  NOSConcurrentUploader
} from '@winman-f2e/nos-js-web';

class MyNOSConcurrentUploader extends NOSConcurrentUploader {
  blockSize = 1024 * 1024 * 10, // 默认：10MB
  shouldUpdateProgressFromBlockUploading = true // 设置是否在每个分块上传一部分也更新整体进度，关闭可以方式暂停的进度回退。但是需要配合设置一个blockSize，才能获得一个比较好的进度体验
}
const upload = new NOSWebClass(MyNOSConcurrentUploader);
```


### 自定义上传逻辑
比如我们需要再上传完成后，额外发送一个请求，校验文件信息，才能确认上传完成


```ts
import {
  NOSWebClass,
  NOSConcurrentUploader
 } from '@winman-f2e/nos-js-web';


export class CustomUploader extends NOSConcurrentUploader {
  public checkRemote = () => {
    return fetch(
      '/check',
      {
        method: 'GET',
        query: {
          objectName: this.signature?.key
        }
      }
    )
  }

  updateProgress (progressInfo) {
    // 未获取信息前，最大 99%;
    progressInfo.percent = Math.min(0.99, progressInfo.percent);
    super.updateProgress.call(this, progressInfo)
  }

  setComplete () {
    return this.checkRemote()
      .then(
        () => {
          super.setComplete.call(this)
        },
        () => {
          this.setError('文件校验有误')
        })
  }
}

// 使用 new NOSWebClass 进行实例化，即会可以在
const upload = new NOSWebClass(CustomUploader);
```

## API
```ts
import {
  NOSClass,
  NOSUploader,
  NOSBlockUploader,
  NOSDefaultUploader,
  NOSConcurrentUploader,

  WebAdapter,
  NOSWebClass,
  NOSWeb,
  BlockNOSWeb,
  ConcurrentNOSWeb
} from '@winman-f2e/nos-js-web';
```

### 来自 @winman-f2e/nos-js
参考 [@winman-f2e/nos-js API](../nos-js/README.md#api)
- NOSClass
- NOSUploader
- NOSBlockUploader
- NOSDefaultUploader
- NOSConcurrentUploader

### 特有API
#### WebAdapter 和 NOSWebClass
- WebAdapter 是浏览器端的适配器工具实现的集合
- NOSWebClass 是 WebAdapter 装配到 NOSClass 后的产物

一般直接使用NOSWebClass即可，如：
```ts
const upload = new NOSWebClass(CustomUploader);
```
即可完成浏览器端自定义上传器。


#### NOSWeb
```ts
export const NOSWeb = new NOSWebClass(NOSDefaultUploader);
```
使用方法：[基础使用例子](#基础使用例子)

#### BlockNOSWeb
使用方法同NOSWeb
```ts
export const BlockNOSWeb = new NOSWebClass(NOSBlockUploader);
```

#### ConcurrentNOSWeb
使用方法同NOSWeb
```ts
export const ConcurrentNOSWeb = new NOSWebClass(NOSConcurrentUploader);
```

## RichMediaURL 富媒体url处理工具
[@winman-f2e/nos-js#richmediaurl-富媒体url处理工具](https://www.npmjs.com/package/@winman-f2e/nos-js#richmediaurl-富媒体url处理工具)
直接从 @winman-f2e/nos-js 包中导出

---
_Source: https://npm.io/package/@winman-f2e/nos-js-web · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
