Socket
Socket
Sign inDemoInstall

qiniu

Package Overview
Dependencies
118
Maintainers
2
Versions
60
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 7.9.0 to 7.10.0

qiniu/httpc/endpoint.js

5

CHANGELOG.md
## CHANGE LOG
## 7.10.0
- 对象存储,上传支持双活
- 对象存储,上传回调支持 Promise 风格
- 对象存储,修复分片上传v2在创建 uploadId 失败后仍尝试上传
## 7.9.0

@@ -3,0 +8,0 @@ - 对象存储,修复无法对 key 为空字符串的对象进行操作

301

index.d.ts

@@ -146,2 +146,7 @@ /**

zoneExpire?: number;
/**
* @default null
*/
regionsProvider?: httpc.RegionsProvider;
}

@@ -165,2 +170,7 @@ class Config implements ConfigOptions {

export declare namespace form_up {
type UploadResult = {
data: any;
resp: IncomingMessage;
}
class FormUploader {

@@ -175,7 +185,13 @@ conf: conf.Config;

* @param key
* @param rsStream
* @param fsStream
* @param putExtra
* @param callback
*/
putStream(uploadToken: string, key: string | null, rsStream: NodeJS.ReadableStream, putExtra: PutExtra | null, callback: callback): void;
putStream(
uploadToken: string,
key: string | null,
fsStream: NodeJS.ReadableStream,
putExtra: PutExtra | null,
callback: callback
): Promise<UploadResult>;

@@ -190,3 +206,9 @@ /**

*/
put(uploadToken: string, key: string | null, body: any, putExtra: PutExtra | null, callback: callback): void;
put(
uploadToken: string,
key: string | null,
body: any,
putExtra: PutExtra | null,
callback: callback
): Promise<UploadResult>;

@@ -200,3 +222,8 @@ /**

*/
putWithoutKey(uploadToken: string, body: any, putExtra: PutExtra | null, callback: callback): void;
putWithoutKey(
uploadToken: string,
body: any,
putExtra: PutExtra | null,
callback: callback
): Promise<UploadResult>;

@@ -211,3 +238,9 @@ /**

*/
putFile(uploadToken: string, key: string | null, localFile: string, putExtra: PutExtra | null, callback: callback): void;
putFile(
uploadToken: string,
key: string | null,
localFile: string,
putExtra: PutExtra | null,
callback: callback
): Promise<UploadResult>;

@@ -221,3 +254,8 @@ /**

*/
putFileWithoutKey(uploadToken: string, localFile: string, putExtra: PutExtra | null, callback: callback): void;
putFileWithoutKey(
uploadToken: string,
localFile: string,
putExtra: PutExtra | null,
callback: callback
): Promise<UploadResult>;
}

@@ -234,3 +272,3 @@

*/
params: any;
params: Record<string, string>;

@@ -271,2 +309,7 @@ /**

export declare namespace resume_up {
type UploadResult = {
data: any;
resp: IncomingMessage;
}
class ResumeUploader {

@@ -286,3 +329,10 @@ config: conf.Config;

*/
putStream(uploadToken: string, key: string | null, rsStream: NodeJS.ReadableStream, rsStreamLen: number, putExtra: PutExtra | null, callback: callback): void;
putStream(
uploadToken: string,
key: string | null,
rsStream: NodeJS.ReadableStream,
rsStreamLen: number,
putExtra: PutExtra | null,
callback: callback
): Promise<UploadResult>;

@@ -297,3 +347,9 @@ /**

*/
putFile(uploadToken: string, key: string | null, localFile: string, putExtra: PutExtra | null, callback: callback): void;
putFile(
uploadToken: string,
key: string | null,
localFile: string,
putExtra: PutExtra | null,
callback: callback
): Promise<UploadResult>;

@@ -307,3 +363,8 @@ /**

*/
putFileWithoutKey(uploadToken: string, localFile: string, putExtra: PutExtra | null, callback: callback): void;
putFileWithoutKey(
uploadToken: string,
localFile: string,
putExtra: PutExtra | null,
callback: callback
): Promise<UploadResult>;
}

@@ -445,3 +506,3 @@

interface RespWrapperOptions<T = any> {
interface ResponseWrapperOptions<T = any> {
data: T;

@@ -451,6 +512,7 @@ resp: IncomingMessage;

class RespWrapper<T = any> {
// responseWrapper.js
class ResponseWrapper<T = any> {
data: T;
resp: IncomingMessage;
constructor(options: RespWrapperOptions);
constructor(options: ResponseWrapperOptions);
ok(): boolean;

@@ -460,2 +522,3 @@ needRetry(): boolean;

// middleware package
namespace middleware {

@@ -465,4 +528,4 @@ interface Middleware {

request: ReqOpts<T>,
next: (reqOpts: ReqOpts<T>) => Promise<RespWrapper<T>>
): Promise<RespWrapper<T>>;
next: (reqOpts: ReqOpts<T>) => Promise<ResponseWrapper<T>>
): Promise<ResponseWrapper<T>>;
}

@@ -477,3 +540,3 @@

middlewares: Middleware[],
handler: (reqOpts: ReqOpts<T>) => Promise<RespWrapper<T>>
handler: (reqOpts: ReqOpts<T>) => Promise<ResponseWrapper<T>>
);

@@ -488,4 +551,4 @@

request: httpc.ReqOpts<T>,
next: (reqOpts: httpc.ReqOpts<T>) => Promise<httpc.RespWrapper<T>>
): Promise<httpc.RespWrapper<T>>;
next: (reqOpts: httpc.ReqOpts<T>) => Promise<httpc.ResponseWrapper<T>>
): Promise<httpc.ResponseWrapper<T>>;
}

@@ -534,4 +597,4 @@

request: httpc.ReqOpts<T>,
next: (reqOpts: httpc.ReqOpts<T>) => Promise<httpc.RespWrapper<T>>
): Promise<httpc.RespWrapper<T>>;
next: (reqOpts: httpc.ReqOpts<T>) => Promise<httpc.ResponseWrapper<T>>
): Promise<httpc.ResponseWrapper<T>>;

@@ -547,3 +610,3 @@ /**

err: Error | null,
respWrapper: RespWrapper<T>,
respWrapper: ResponseWrapper<T>,
reqOpts: ReqOpts<T>

@@ -554,2 +617,3 @@ ): boolean;

// client.js
interface HttpClientOptions {

@@ -581,7 +645,194 @@ httpAgent?: HttpAgent;

constructor(options: HttpClientOptions)
sendRequest(requestOptions: ReqOpts): Promise<RespWrapper>
get(getOptions: GetOptions): Promise<RespWrapper>
post(postOptions: PostOptions): Promise<RespWrapper>
put(putOptions: PutOptions): Promise<RespWrapper>
sendRequest(requestOptions: ReqOpts): Promise<ResponseWrapper>
get(getOptions: GetOptions): Promise<ResponseWrapper>
post(postOptions: PostOptions): Promise<ResponseWrapper>
put(putOptions: PutOptions): Promise<ResponseWrapper>
}
// endpoint.js
interface EndpointOptions {
defaultScheme?: string;
}
interface EndpointPersistInfo {
host: string;
defaultScheme: string;
}
class Endpoint {
static fromPersistInfo(persistInfo: EndpointPersistInfo): Endpoint;
host: string;
defaultScheme: string;
constructor(host: string, options?: EndpointOptions);
getValue(options?: {scheme?: string}): string;
get persistInfo(): EndpointPersistInfo;
}
// region.js
enum SERVICE_NAME {
UC = 'uc',
UP = 'up',
IO = 'io',
RS = 'rs',
RSF = 'rsf',
API = 'api',
S3 = 's3'
}
interface RegionOptions {
regionId?: string;
s3RegionId?: string;
services?: Record<string, Endpoint[]>;
ttl?: number;
createTime?: Date;
}
interface RegionFromZoneOptions {
regionId?: string;
s3RegionId?: string;
ttl?: number;
isPreferCdnHost?: boolean;
}
interface RegionFromRegionIdOptions {
s3RegionId?: string;
ttl?: number;
createTime?: Date;
extendedServices?: Record<SERVICE_NAME | string, Endpoint[]>
}
interface RegionPersistInfo {
regionId?: string;
s3RegionId?: string;
services: Record<SERVICE_NAME | string, EndpointPersistInfo[]>;
ttl: number;
createTime: number;
}
interface QueryRegionsRespData {
region: string;
ttl: number;
s3: {
domains: string[];
region_alias: string;
};
uc: {
domains: string[];
};
up: {
domains: string[];
};
io: {
domains: string[];
};
rs: {
domains: string[];
};
rsf: {
domains: string[];
};
api: {
domains: string[];
};
}
class Region {
static fromZone(zone: conf.Zone, options?: RegionFromZoneOptions): Region;
static fromRegionId(regionId: string, options?: RegionFromRegionIdOptions): Region;
static fromPersistInfo(persistInfo: RegionPersistInfo): Region;
static fromQueryData(data: QueryRegionsRespData): Region;
// non-unique
regionId?: string;
s3RegionId?: string;
services: Record<SERVICE_NAME | string, Endpoint[]>
ttl: number;
createTime: Date;
constructor(options: RegionOptions);
get isLive(): boolean;
get persistInfo(): RegionPersistInfo;
}
// endpointProvider.js
interface EndpointsProvider {
getEndpoints(): Promise<Endpoint[]>
}
interface MutableEndpointsProvider extends EndpointsProvider {
setEndpoints(endpoints: Endpoint[]): Promise<void>
}
class StaticEndpointsProvider implements EndpointsProvider {
static fromRegion(region: Region, serviceName: SERVICE_NAME | string): StaticEndpointsProvider;
constructor(endpoints: Endpoint[]);
getEndpoints(): Promise<Endpoint[]>;
}
// regionsProvider.js
interface RegionsProvider {
getRegions(): Promise<Region[]>
}
interface MutableRegionsProvider extends RegionsProvider {
setRegions(regions: Region[]): Promise<void>
}
// StaticRegionsProvider
class StaticRegionsProvider implements RegionsProvider {
regions: Region[];
constructor(regions: Region[]);
getRegions(): Promise<Region[]>;
}
// CachedRegionsProviderOptions
interface CachedRegionsProviderOptions {
cacheKey: string;
baseRegionsProvider: RegionsProvider;
persistPath?: string;
shrinkInterval?: number; // ms
}
class CachedRegionsProvider implements MutableRegionsProvider {
cacheKey: string;
baseRegionsProvider: RegionsProvider;
lastShrinkAt: Date;
shrinkInterval: number;
constructor(
options: CachedRegionsProviderOptions
);
setRegions(regions: Region[]): Promise<void>;
getRegions(): Promise<Region[]>;
}
// QueryRegionsProvider
interface QueryRegionsProviderOptions {
accessKey: string;
bucketName: string;
endpointsProvider: EndpointsProvider;
}
class QueryRegionsProvider implements RegionsProvider {
accessKey: string;
bucketName: string;
endpointsProvider: EndpointsProvider;
constructor(options: QueryRegionsProviderOptions);
getRegions(): Promise<Region[]>;
}
}

@@ -588,0 +839,0 @@

@@ -14,3 +14,11 @@ module.exports = {

HttpClient: require('./qiniu/httpc/client').HttpClient,
ResponseWrapper: require('./qiniu/httpc/responseWrapper').ResponseWrapper
ResponseWrapper: require('./qiniu/httpc/responseWrapper').ResponseWrapper,
Endpoint: require('./qiniu/httpc/endpoint').Endpoint,
StaticEndpointsProvider: require('./qiniu/httpc/endpointsProvider').StaticEndpointsProvider,
SERVICE_NAME: require('./qiniu/httpc/region').SERVICE_NAME,
Region: require('./qiniu/httpc/region').Region,
StaticRegionsProvider: require('./qiniu/httpc/regionsProvider').StaticRegionsProvider,
CachedRegionsProvider: require('./qiniu/httpc/regionsProvider').CachedRegionsProvider,
QueryRegionsProvider: require('./qiniu/httpc/regionsProvider').QueryRegionsProvider,
ChainedRegionsProvider: require('./qiniu/httpc/regionsProvider').ChainedRegionsProvider
},

@@ -24,4 +32,4 @@ rpc: require('./qiniu/rpc.js'),

sms: {
message: require('./qiniu/sms/message.js'),
message: require('./qiniu/sms/message.js')
}
};

4

package.json
{
"name": "qiniu",
"version": "7.9.0",
"version": "7.10.0",
"description": "Node wrapper for Qiniu Resource (Cloud) Storage API",

@@ -62,3 +62,3 @@ "main": "index.js",

"tunnel-agent": "^0.6.0",
"urllib": "^2.34.1"
"urllib": "^2.41.0"
},

@@ -65,0 +65,0 @@ "devDependencies": {

@@ -57,3 +57,3 @@ const os = require('os');

this.useHttpsDomain = !!(options.useHttpsDomain || false);
// use cdn accerlated domains
// use cdn accerlated domains, this is not work with auto query region
this.useCdnDomain = !!(options.useCdnDomain && true);

@@ -64,2 +64,4 @@ // zone of the bucket

this.zoneExpire = options.zoneExpire || -1;
// only available with upload for now
this.regionsProvider = options.regionsProvider || null;
};

@@ -66,0 +68,0 @@

@@ -53,9 +53,2 @@ const http = require('http');

/**
* Wrapped result of request
* @typedef {Object} RespWrapper
* @property {*} data
* @property {http.IncomingMessage} resp
*/
/**
*

@@ -62,0 +55,0 @@ * @param {ReqOpts} requestOptions

@@ -22,4 +22,4 @@ /**

* @param {Middleware[]} middlewares
* @param {function(ReqOpts):Promise<RespWrapper>} handler
* @return {function(ReqOpts):Promise<RespWrapper>}
* @param {function(ReqOpts):Promise<ResponseWrapper>} handler
* @return {function(ReqOpts):Promise<ResponseWrapper>}
*/

@@ -26,0 +26,0 @@ exports.composeMiddlewares = function (middlewares, handler) {

const middleware = require('./base');
const URL = require('url').URL;
/**

@@ -9,3 +11,3 @@ * @class

* @param {number} [retryDomainsOptions.maxRetryTimes]
* @param {function(Error || null, RespWrapper || null, ReqOpts):boolean} [retryDomainsOptions.retryCondition]
* @param {function(Error || null, ResponseWrapper || null, ReqOpts):boolean} [retryDomainsOptions.retryCondition]
* @constructor

@@ -27,3 +29,3 @@ */

* @param {Error || null} err
* @param {RespWrapper || null} respWrapper
* @param {ResponseWrapper || null} respWrapper
* @param {ReqOpts} reqOpts

@@ -44,3 +46,3 @@ * @return {boolean}

* @param {ReqOpts} reqOpts
* @param {function(ReqOpts):Promise<RespWrapper>} next
* @param {function(ReqOpts):Promise<ResponseWrapper>} next
* @return {Promise<RespWrapper>}

@@ -62,3 +64,6 @@ */

this._retriedTimes = 0;
url.hostname = domains.shift();
const domain = domains.shift();
const [hostname, port] = domain.split(':');
url.hostname = hostname;
url.port = port || url.port;
reqOpts.url = url.toString();

@@ -65,0 +70,0 @@ return true;

@@ -1,26 +0,56 @@

const conf = require('../conf');
const util = require('../util');
const rpc = require('../rpc');
const fs = require('fs');
const path = require('path');
const Readable = require('stream').Readable;
const getCrc32 = require('crc32');
const path = require('path');
const mime = require('mime');
const Readable = require('stream').Readable;
const formstream = require('formstream');
const conf = require('../conf');
const util = require('../util');
const rpc = require('../rpc');
const {
prepareRegionsProvider,
doWorkWithRetry,
ChangeEndpointRetryPolicy,
ChangeRegionRetryPolicy
} = require('./internal');
exports.FormUploader = FormUploader;
exports.PutExtra = PutExtra;
/**
* @class
* @param {conf.Config} [config]
* @constructor
*/
function FormUploader (config) {
this.config = config || new conf.Config();
// RetryPolicy API sign isn't stable not export to user
// Internal usage only
this.retryPolicies = [
new ChangeEndpointRetryPolicy(),
new ChangeRegionRetryPolicy()
];
}
// 上传可选参数
// @params fname 请求体中的文件的名称
// @params params 额外参数设置,参数名称必须以x:开头
// @param mimeType 指定文件的mimeType
// @param crc32 指定文件的crc32值
// @param checkCrc 指定是否检测文件的crc32值
// @param metadata 元数据设置,参数名称必须以 x-qn-meta-${name}: 开头
function PutExtra (fname, params, mimeType, crc32, checkCrc, metadata) {
/**
* 上传可选参数
* @param {string} [fname] 请求体中的文件的名称
* @param {Object} [params] 额外参数设置,参数名称必须以x:开头
* @param {string} [mimeType] 指定文件的mimeType
* @param {string} [crc32] 指定文件的crc32值
* @param {number | boolean} [checkCrc] 指定是否检测文件的crc32值
* @param {Object} [metadata] 元数据设置,参数名称必须以 x-qn-meta-${name}: 开头
*/
function PutExtra (
fname,
params,
mimeType,
crc32,
checkCrc,
metadata
) {
this.fname = fname || '';

@@ -34,78 +64,187 @@ this.params = params || {};

FormUploader.prototype.putStream = function (uploadToken, key, fsStream,
putExtra, callbackFunc) {
putExtra = putExtra || new PutExtra();
if (!putExtra.mimeType) {
putExtra.mimeType = 'application/octet-stream';
}
/**
* @callback reqCallback
*
* @param { Error } err
* @param { Object } ret
* @param { http.IncomingMessage } info
*/
if (!putExtra.fname) {
putExtra.fname = key || 'fname';
}
/**
* @typedef UploadResult
* @property {any} data
* @property {http.IncomingMessage} resp
*/
/**
* @param {string} uploadToken
* @param {string | null} key
* @param {stream.Readable} fsStream
* @param {PutExtra | null} putExtra
* @param {reqCallback} callbackFunc
* @returns {Promise<UploadResult>}
*/
FormUploader.prototype.putStream = function (
uploadToken,
key,
fsStream,
putExtra,
callbackFunc
) {
const preferScheme = this.config.useHttpsDomain ? 'https' : 'http';
// PutExtra
putExtra = getDefaultPutExtra(
putExtra,
{
key
}
);
fsStream.on('error', function (err) {
// callbackFunc
callbackFunc(err, null, null);
});
var accessKey = util.getAKFromUptoken(uploadToken);
var bucket = util.getBucketFromUptoken(uploadToken);
// RegionsProvider
return prepareRegionsProvider({
config: this.config,
bucketName: util.getBucketFromUptoken(uploadToken),
accessKey: util.getAKFromUptoken(uploadToken)
})
.then(regionsProvider => {
return doWorkWithRetry({
workFn: sendPutReq,
util.prepareZone(this, accessKey, bucket, function (err, ctx) {
if (err) {
callbackFunc(err, null, null);
return;
}
createMultipartForm(uploadToken, key, fsStream, putExtra, function (postForm) {
putReq(ctx.config, postForm, callbackFunc);
callbackFunc,
regionsProvider,
// stream not support retry
retryPolicies: []
});
});
});
};
function putReq (config, postForm, callbackFunc) {
// set up hosts order
var upHosts = [];
function sendPutReq (endpoint) {
const endpointValue = endpoint.getValue({
scheme: preferScheme
});
if (config.useCdnDomain) {
if (config.zone.cdnUpHosts) {
config.zone.cdnUpHosts.forEach(function (host) {
upHosts.push(host);
});
}
config.zone.srcUpHosts.forEach(function (host) {
upHosts.push(host);
const postForm = createMultipartForm(
uploadToken,
key,
fsStream,
putExtra
);
return new Promise(resolve => {
putReq(
endpointValue,
postForm,
(err, ret, info) => resolve({ err, ret, info })
);
});
} else {
config.zone.srcUpHosts.forEach(function (host) {
upHosts.push(host);
});
config.zone.cdnUpHosts.forEach(function (host) {
upHosts.push(host);
});
}
};
var scheme = config.useHttpsDomain ? 'https://' : 'http://';
var upDomain = scheme + upHosts[0];
/**
* @param {string} upDomain
* @param {formstream} postForm
* @param {reqCallback} callbackFunc
*/
function putReq (upDomain, postForm, callbackFunc) {
rpc.postMultipart(upDomain, postForm, callbackFunc);
}
// 上传字节
//
FormUploader.prototype.put = function (uploadToken, key, body, putExtra,
callbackFunc) {
var fsStream = new Readable();
fsStream.push(body);
fsStream.push(null);
/**
* 上传字节
* @param {string} uploadToken
* @param {string | null} key
* @param {any} body
* @param {PutExtra | null} putExtra
* @param {reqCallback} callbackFunc
* @returns {Promise<UploadResult>}
*/
FormUploader.prototype.put = function (
uploadToken,
key,
body,
putExtra,
callbackFunc
) {
const preferScheme = this.config.useHttpsDomain ? 'https' : 'http';
putExtra = putExtra || new PutExtra();
return this.putStream(uploadToken, key, fsStream, putExtra, callbackFunc);
// initial PutExtra
putExtra = getDefaultPutExtra(
putExtra,
{
key
}
);
// initial RegionsProvider
return prepareRegionsProvider({
config: this.config,
bucketName: util.getBucketFromUptoken(uploadToken),
accessKey: util.getAKFromUptoken(uploadToken)
})
.then(regionsProvider => {
return doWorkWithRetry({
workFn: sendPutReq,
callbackFunc,
regionsProvider,
retryPolicies: this.retryPolicies
});
});
function sendPutReq (endpoint) {
const fsStream = new Readable();
fsStream.push(body);
fsStream.push(null);
const endpointValue = endpoint.getValue({
scheme: preferScheme
});
const postForm = createMultipartForm(
uploadToken,
key,
fsStream,
putExtra
);
return new Promise(resolve => {
putReq(
endpointValue,
postForm,
(err, ret, info) => {
resolve({ err, ret, info });
}
);
});
}
};
FormUploader.prototype.putWithoutKey = function (uploadToken, body, putExtra,
callbackFunc) {
/**
* @param {string} uploadToken
* @param {any} body
* @param {PutExtra | null} putExtra
* @param {reqCallback} callbackFunc
* @returns {Promise<UploadResult>}
*/
FormUploader.prototype.putWithoutKey = function (
uploadToken,
body,
putExtra,
callbackFunc
) {
return this.put(uploadToken, null, body, putExtra, callbackFunc);
};
function createMultipartForm (uploadToken, key, fsStream, putExtra, callbackFunc) {
var postForm = formstream();
/**
* @param {string} uploadToken
* @param {string | null} key
* @param {stream.Readable} fsStream
* @param {PutExtra | null} putExtra
* @returns {formstream}
*/
function createMultipartForm (uploadToken, key, fsStream, putExtra) {
const postForm = formstream();
postForm.field('token', uploadToken);

@@ -115,6 +254,11 @@ if (key != null) {

}
postForm.stream('file', fsStream, putExtra.fname, putExtra.mimeType);
postForm.stream(
'file',
fsStream,
putExtra.fname,
putExtra.mimeType
);
// putExtra params
for (var k in putExtra.params) {
for (const k in putExtra.params) {
if (k.startsWith('x:')) {

@@ -126,3 +270,3 @@ postForm.field(k, putExtra.params[k].toString());

// putExtra metadata
for (var metadataKey in putExtra.metadata) {
for (const metadataKey in putExtra.metadata) {
if (metadataKey.startsWith('x-qn-meta-')) {

@@ -133,3 +277,3 @@ postForm.field(metadataKey, putExtra.metadata[metadataKey].toString());

var fileBody = [];
let fileBody = [];
fsStream.on('data', function (data) {

@@ -139,8 +283,8 @@ fileBody.push(data);

fsStream.on('end', function() {
fsStream.on('end', function () {
if (putExtra.checkCrc) {
if (putExtra.crc32 == null) {
fileBody = Buffer.concat(fileBody);
var bodyCrc32 = parseInt('0x' + getCrc32(fileBody));
postForm.field('crc32', bodyCrc32);
const bodyCrc32 = parseInt('0x' + getCrc32(fileBody));
postForm.field('crc32', bodyCrc32.toString());
} else {

@@ -151,16 +295,25 @@ postForm.field('crc32', putExtra.crc32);

});
callbackFunc(postForm);
return postForm;
}
// 上传本地文件
// @params uploadToken 上传凭证
// @param key 目标文件名
// @param localFile 本地文件路径
// @param putExtra 额外选项
// @param callbackFunc 回调函数
FormUploader.prototype.putFile = function (uploadToken, key, localFile, putExtra,
callbackFunc) {
/** 上传本地文件
* @param {string} uploadToken 上传凭证
* @param {string | null} key 目标文件名
* @param {string} localFile 本地文件路径
* @param {PutExtra | null} putExtra 额外选项
* @param callbackFunc 回调函数
* @returns {Promise<UploadResult>}
*/
FormUploader.prototype.putFile = function (
uploadToken,
key,
localFile,
putExtra,
callbackFunc
) {
const preferScheme = this.config.useHttpsDomain ? 'https' : 'http';
// initial PutExtra
putExtra = putExtra || new PutExtra();
var fsStream = fs.createReadStream(localFile);
if (!putExtra.mimeType) {

@@ -174,8 +327,81 @@ putExtra.mimeType = mime.getType(localFile);

return this.putStream(uploadToken, key, fsStream, putExtra, callbackFunc);
putExtra = getDefaultPutExtra(
putExtra,
{
key
}
);
// initial RegionsProvider
return prepareRegionsProvider({
config: this.config,
bucketName: util.getBucketFromUptoken(uploadToken),
accessKey: util.getAKFromUptoken(uploadToken)
})
.then(regionsProvider => {
return doWorkWithRetry({
workFn: sendPutReq,
callbackFunc,
regionsProvider,
retryPolicies: this.retryPolicies
});
});
function sendPutReq (endpoint) {
const fsStream = fs.createReadStream(localFile);
const endpointValue = endpoint.getValue({
scheme: preferScheme
});
const postForm = createMultipartForm(
uploadToken,
key,
fsStream,
putExtra
);
return new Promise(resolve => {
putReq(
endpointValue,
postForm,
(err, ret, info) => {
resolve({ err, ret, info });
}
);
});
}
};
FormUploader.prototype.putFileWithoutKey = function (uploadToken, localFile,
putExtra, callbackFunc) {
/** 上传本地文件
* @param {string} uploadToken 上传凭证
* @param {string} localFile 本地文件路径
* @param {PutExtra | null} putExtra 额外选项
* @param callbackFunc 回调函数
* @returns {Promise<UploadResult>}
*/
FormUploader.prototype.putFileWithoutKey = function (
uploadToken,
localFile,
putExtra,
callbackFunc
) {
return this.putFile(uploadToken, null, localFile, putExtra, callbackFunc);
};
/**
* @param {PutExtra} putExtra
* @param {Object} options
* @param {string} options.key
* @return {PutExtra}
*/
function getDefaultPutExtra (putExtra, options) {
putExtra = putExtra || new PutExtra();
if (!putExtra.mimeType) {
putExtra.mimeType = 'application/octet-stream';
}
if (!putExtra.fname) {
putExtra.fname = options.key || 'fname';
}
return putExtra;
}

@@ -1,7 +0,5 @@

const conf = require('../conf');
const util = require('../util');
const rpc = require('../rpc');
const fs = require('fs');
const path = require('path');
const mime = require('mime');
const fs = require('fs');
const getCrc32 = require('crc32');

@@ -11,7 +9,35 @@ const destroy = require('destroy');

const conf = require('../conf');
const util = require('../util');
const rpc = require('../rpc');
const {
prepareRegionsProvider,
doWorkWithRetry,
TokenExpiredRetryPolicy,
ChangeEndpointRetryPolicy,
ChangeRegionRetryPolicy
} = require('./internal');
const { StaticEndpointsProvider } = require('../httpc/endpointsProvider');
const { Endpoint } = require('../httpc/endpoint');
exports.ResumeUploader = ResumeUploader;
exports.PutExtra = PutExtra;
/**
* @param {conf.Config} [config]
* @constructor
*/
function ResumeUploader (config) {
this.config = config || new conf.Config();
/**
* Internal usage only for now.
* @readonly
*/
this.retryPolicies = [
new TokenExpiredRetryPolicy(),
new ChangeEndpointRetryPolicy(),
new ChangeRegionRetryPolicy()
];
}

@@ -22,5 +48,5 @@

*
* @param { Error } err
* @param { Object } ret
* @param { http.IncomingMessage } info
* @param {Error} err
* @param {Object} ret
* @param {http.IncomingMessage} info
*/

@@ -31,17 +57,17 @@

*
* @param { number } uploadBytes
* @param { number } totalBytes
* @param {number} uploadBytes
* @param {number} totalBytes
*/
/**
* 上传可选参数
* @param { string } fname 请求体中的文件的名称
* @param { Object } params 额外参数设置,参数名称必须以x:开头
* @param { string | null } mimeType 指定文件的mimeType
* @param { string | null } resumeRecordFile 断点续传的已上传的部分信息记录文件路径
* @param { progressCallback } progressCallback 上传进度回调,回调参数为 (uploadBytes, totalBytes)
* @param { number } partSize 分片上传v2必传字段 默认大小为4MB 分片大小范围为1 MB - 1 GB
* @param { 'v1' | 'v2' } version 分片上传版本 目前支持v1/v2版本 默认v1
* @param { Object } metadata 元数据设置,参数名称必须以 x-qn-meta-${name}: 开头
*/
* 上传可选参数
* @param {string} [fname] 请求体中的文件的名称
* @param {Object} [params] 额外参数设置,参数名称必须以x:开头
* @param {string | null} [mimeType] 指定文件的mimeType
* @param {string | null} [resumeRecordFile] 断点续传的已上传的部分信息记录文件路径
* @param {function(number, number):void} [progressCallback] 上传进度回调,回调参数为 (uploadBytes, totalBytes)
* @param {number} [partSize] 分片上传v2必传字段 默认大小为4MB 分片大小范围为1 MB - 1 GB
* @param {'v1' | 'v2'} [version] 分片上传版本 目前支持v1/v2版本 默认v1
* @param {Object} [metadata] 元数据设置,参数名称必须以 x-qn-meta-${name}: 开头
*/
function PutExtra (

@@ -67,2 +93,17 @@ fname,

/**
* @typedef UploadResult
* @property {any} data
* @property {http.IncomingMessage} resp
*/
/**
* @param {string} uploadToken
* @param {string | null} key
* @param {stream.Readable} rsStream
* @param {number} rsStreamLen
* @param {PutExtra} putExtra
* @param {reqCallback} callbackFunc
* @return {Promise<UploadResult>}
*/
ResumeUploader.prototype.putStream = function (

@@ -76,57 +117,96 @@ uploadToken,

) {
putExtra = putExtra || new PutExtra();
if (!putExtra.mimeType) {
putExtra.mimeType = 'application/octet-stream';
}
const preferScheme = this.config.useHttpsDomain ? 'https' : 'http';
const isValidCallback = typeof callbackFunc === 'function';
if (!putExtra.fname) {
putExtra.fname = key || '?';
}
putExtra = getDefaultPutExtra(
putExtra,
{
key
}
);
if (!putExtra.version) {
putExtra.version = 'v1';
}
rsStream.on('error', function (err) {
// callbackFunc
callbackFunc(err, null, null);
// callbackFunc
isValidCallback && callbackFunc(err, null, null);
destroy(rsStream);
});
const accessKey = util.getAKFromUptoken(uploadToken);
const bucket = util.getBucketFromUptoken(uploadToken);
return prepareRegionsProvider({
config: this.config,
bucketName: util.getBucketFromUptoken(uploadToken),
accessKey: util.getAKFromUptoken(uploadToken)
})
.then(regionsProvider => {
const resumeInfo = getResumeRecordInfo(putExtra.resumeRecordFile);
let preferredEndpointsProvider;
if (resumeInfo && Array.isArray(resumeInfo.upDomains)) {
preferredEndpointsProvider = new StaticEndpointsProvider(
resumeInfo.upDomains.map(d => new Endpoint(d, { defaultScheme: preferScheme }))
);
}
return doWorkWithRetry({
workFn: sendPutReq,
util.prepareZone(this, accessKey, bucket, function (err, ctx) {
if (err) {
callbackFunc(err, null, null);
destroy(rsStream);
return;
}
putReq(ctx.config, uploadToken, key, rsStream, rsStreamLen, putExtra, callbackFunc);
});
};
function putReq (config, uploadToken, key, rsStream, rsStreamLen, putExtra, callbackFunc) {
// set up hosts order
const upHosts = [];
if (config.useCdnDomain) {
if (config.zone.cdnUpHosts) {
config.zone.cdnUpHosts.forEach(function (host) {
upHosts.push(host);
callbackFunc,
regionsProvider,
// use resume upDomain firstly
preferredEndpointsProvider: preferredEndpointsProvider,
// stream not support retry
retryPolicies: []
});
}
config.zone.srcUpHosts.forEach(function (host) {
upHosts.push(host);
});
} else {
config.zone.srcUpHosts.forEach(function (host) {
upHosts.push(host);
function sendPutReq (endpoint) {
endpoint = Object.create(endpoint);
endpoint.defaultScheme = preferScheme;
return new Promise(resolve => {
putReq(
endpoint,
uploadToken,
key,
rsStream,
rsStreamLen,
putExtra,
(err, ret, info) => resolve({ err, ret, info }));
});
config.zone.cdnUpHosts.forEach(function (host) {
upHosts.push(host);
});
}
const scheme = config.useHttpsDomain ? 'https://' : 'http://';
const upDomain = scheme + upHosts[0];
};
/**
* @param {string} resumeRecordFilePath
* @returns {undefined | Object.<string, any>}
*/
function getResumeRecordInfo (resumeRecordFilePath) {
// get resume record info
let result;
// read resumeRecordFile
if (resumeRecordFilePath) {
try {
const resumeRecords = fs.readFileSync(resumeRecordFilePath).toString();
result = JSON.parse(resumeRecords);
} catch (e) {
e.code !== 'ENOENT' && console.error(e);
}
}
return result;
}
/**
* @param {Endpoint} upEndpoint
* @param {string} uploadToken
* @param {string | null} key
* @param {ReadableStream} rsStream
* @param {number} rsStreamLen
* @param {PutExtra} putExtra
* @param {reqCallback} callbackFunc
*/
function putReq (
upEndpoint,
uploadToken,
key,
rsStream,
rsStreamLen,
putExtra,
callbackFunc
) {
// make block stream

@@ -139,71 +219,71 @@ const blkStream = rsStream.pipe(new BlockStream({

// get resume record info
let blkputRets = null;
const blkputRets = getResumeRecordInfo(putExtra.resumeRecordFile);
const totalBlockNum = Math.ceil(rsStreamLen / putExtra.partSize);
// read resumeRecordFile
if (putExtra.resumeRecordFile) {
try {
const resumeRecords = fs.readFileSync(putExtra.resumeRecordFile).toString();
blkputRets = JSON.parse(resumeRecords);
} catch (e) {
console.error(e);
}
}
// upload parts
// select upload version
/**
* @type {function(SourceOptions, UploadOptions, reqCallback)}
*/
let doPutReq;
if (putExtra.version === 'v1') {
putReqV1(
{
blkputRets,
rsStream,
rsStreamLen,
blkStream,
totalBlockNum
},
{
key,
upDomain,
uploadToken,
putExtra
},
callbackFunc
);
doPutReq = putReqV1;
} else if (putExtra.version === 'v2') {
putReqV2(
{
blkputRets,
blkStream,
totalBlockNum,
rsStreamLen,
rsStream
},
{
upDomain,
uploadToken,
key,
putExtra
},
callbackFunc
);
doPutReq = putReqV2;
} else {
throw new Error('part upload version number error');
}
// upload parts
doPutReq(
{
blkputRets,
rsStream,
rsStreamLen,
blkStream,
totalBlockNum
},
{
upEndpoint,
uploadToken,
key,
putExtra
},
function (err, ret, info) {
if (info.statusCode === 200 && putExtra.resumeRecordFile) {
try {
fs.unlinkSync(putExtra.resumeRecordFile);
} catch (_e) {
// ignore
}
}
callbackFunc(err, ret, info);
}
);
}
/**
* @param { Object } sourceOptions
* @param { Object[] | null } sourceOptions.blkputRets
* @param { ReadableStream } sourceOptions.rsStream
* @param { BlockStream } sourceOptions.blkStream
* @param { number } sourceOptions.rsStreamLen
* @param { number } sourceOptions.totalBlockNum
* @param { Object } uploadOptions
* @param { string } uploadOptions.key
* @param { string } uploadOptions.upDomain
* @param { string } uploadOptions.uploadToken
* @param { PutExtra } uploadOptions.putExtra
* @param { reqCallback } callbackFunc
* @typedef SourceOptions
* @property { Object.<string, any> | undefined } blkputRets
* @property { ReadableStream } rsStream
* @property { BlockStream } blkStream
* @property { number } rsStreamLen
* @property { number } totalBlockNum
*/
/**
* @typedef UploadOptions
* @property { string | null } key
* @property { Endpoint } upEndpoint
* @property { string } uploadToken
* @property { PutExtra } putExtra
*/
/**
* @param {SourceOptions} sourceOptions
* @param {UploadOptions} uploadOptions
* @param {reqCallback} callbackFunc
* @returns { Promise<UploadResult> }
*/
function putReqV1 (sourceOptions, uploadOptions, callbackFunc) {
const {
blkputRets,
rsStream,

@@ -214,5 +294,6 @@ blkStream,

} = sourceOptions;
let blkputRets = sourceOptions.blkputRets;
const {
upEndpoint,
key,
upDomain,
uploadToken,

@@ -224,5 +305,20 @@ putExtra

const finishedCtxList = [];
const finishedBlkPutRets = [];
const finishedBlkPutRets = {
upDomains: [],
parts: []
};
// backward compatibility with ≤ 7.9.0
if (Array.isArray(blkputRets)) {
blkputRets = {
upDomains: [],
parts: []
};
}
if (blkputRets && Array.isArray(blkputRets.upDomains)) {
finishedBlkPutRets.upDomains = blkputRets.upDomains;
}
finishedBlkPutRets.upDomains.push(upEndpoint.host);
// upload parts
const upDomains = upEndpoint.getValue();
let readLen = 0;

@@ -235,4 +331,9 @@ let curBlock = 0;

// check uploaded parts
if (blkputRets && blkputRets.length > 0 && blkputRets[curBlock]) {
const blkputRet = blkputRets[curBlock];
if (
blkputRets &&
blkputRets.parts &&
blkputRets.parts.length > 0 &&
blkputRets.parts[curBlock]
) {
const blkputRet = blkputRets.parts[curBlock];
let expiredAt = blkputRet.expired_at;

@@ -244,3 +345,3 @@ // make sure the ctx at least has one day expiration

finishedCtxList.push(blkputRet.ctx);
finishedBlkPutRets.push(blkputRet);
finishedBlkPutRets.parts.push(blkputRet);
}

@@ -253,3 +354,3 @@ }

mkblkReq(
upDomain,
upDomains,
uploadToken,

@@ -269,3 +370,3 @@ chunk,

finishedCtxList.push(blkputRet.ctx);
finishedBlkPutRets.push(blkputRet);
finishedBlkPutRets.parts.push(blkputRet);
if (putExtra.resumeRecordFile) {

@@ -282,3 +383,3 @@ const contents = JSON.stringify(finishedBlkPutRets);

if (finishedCtxList.length === totalBlockNum) {
mkfileReq(upDomain, uploadToken, rsStreamLen, finishedCtxList, key, putExtra, callbackFunc);
mkfileReq(upDomains, uploadToken, rsStreamLen, finishedCtxList, key, putExtra, callbackFunc);
isSent = true;

@@ -293,3 +394,3 @@ }

if (!isSent && rsStreamLen === 0) {
mkfileReq(upDomain, uploadToken, rsStreamLen, finishedCtxList, key, putExtra, callbackFunc);
mkfileReq(upDomains, uploadToken, rsStreamLen, finishedCtxList, key, putExtra, callbackFunc);
}

@@ -301,14 +402,6 @@ destroy(rsStream);

/**
* @param { Object } sourceOptions
* @param { Object | null } sourceOptions.blkputRets
* @param { ReadableStream } sourceOptions.rsStream
* @param { BlockStream } sourceOptions.blkStream
* @param { number } sourceOptions.rsStreamLen
* @param { number } sourceOptions.totalBlockNum
* @param { Object } uploadOptions
* @param { string } uploadOptions.key
* @param { string } uploadOptions.upDomain
* @param { string } uploadOptions.uploadToken
* @param { PutExtra } uploadOptions.putExtra
* @param { reqCallback } callbackFunc
* @param {SourceOptions} sourceOptions
* @param {UploadOptions} uploadOptions
* @param {reqCallback} callbackFunc
* @returns { Promise<UploadResult> }
*/

@@ -324,5 +417,5 @@ function putReqV2 (sourceOptions, uploadOptions, callbackFunc) {

const {
upEndpoint,
uploadToken,
key,
upDomain,
putExtra

@@ -334,2 +427,3 @@ } = uploadOptions;

const finishedEtags = {
upDomains: [],
etags: [],

@@ -339,7 +433,8 @@ uploadId: '',

};
if (blkputRets !== null) {
if (blkputRets && Array.isArray(blkputRets.upDomains)) {
// check etag expired or not
const expiredAt = blkputRets.expiredAt;
const timeNow = Date.now() / 1000;
if (expiredAt > timeNow && blkputRets.uploadId !== '') {
if (expiredAt > timeNow && blkputRets.uploadId) {
finishedEtags.upDomains = blkputRets.upDomains;
finishedEtags.etags = blkputRets.etags;

@@ -351,3 +446,5 @@ finishedEtags.uploadId = blkputRets.uploadId;

}
finishedEtags.upDomains.push(upEndpoint.host);
const upDomain = upEndpoint.getValue();
const bucket = util.getBucketFromUptoken(uploadToken);

@@ -371,2 +468,8 @@ const encodedObjectName = key ? util.urlsafeBase64Encode(key) : '~';

/**
* @param {string} upDomain
* @param {string} uploadToken
* @param {Buffer | string} blkData
* @param {reqCallback} callbackFunc
*/
function mkblkReq (upDomain, uploadToken, blkData, callbackFunc) {

@@ -382,2 +485,11 @@ const requestURI = upDomain + '/mkblk/' + blkData.length;

/**
* @param {string} upDomain
* @param {string} uploadToken
* @param {number} fileSize
* @param {string[]} ctxList
* @param {string | null} key
* @param putExtra
* @param callbackFunc
*/
function mkfileReq (

@@ -403,3 +515,3 @@ upDomain,

if (putExtra.params) {
// putExtra params
// putExtra params
for (const k in putExtra.params) {

@@ -430,16 +542,26 @@ if (k.startsWith('x:') && putExtra.params[k]) {

const postBody = ctxList.join(',');
rpc.post(requestURI, postBody, headers, function (err, ret, info) {
if (info.statusCode === 200 || info.statusCode === 701) {
if (putExtra.resumeRecordFile) {
try {
fs.unlinkSync(putExtra.resumeRecordFile);
} catch (_e) {
// ignore
}
}
}
callbackFunc(err, ret, info);
});
rpc.post(requestURI, postBody, headers, callbackFunc);
}
/**
* @typedef FinishedEtags
* @property {{etag: string, partNumber: number}[]}etags
* @property {string} uploadId
* @property {number} expiredAt
*/
/**
* @param {string} uploadToken
* @param {string} bucket
* @param {string} encodedObjectName
* @param {string} upDomain
* @param {BlockStream} blkStream
* @param {FinishedEtags} finishedEtags
* @param {number} finishedBlock
* @param {number} totalBlockNum
* @param {PutExtra} putExtra
* @param {number} rsStreamLen
* @param {stream.Readable} rsStream
* @param {reqCallback} callbackFunc
*/
function initReq (

@@ -467,2 +589,3 @@ uploadToken,

callbackFunc(err, ret, info);
return;
}

@@ -476,2 +599,16 @@ finishedEtags.expiredAt = ret.expireAt;

/**
* @param {string} uploadToken
* @param {string} bucket
* @param {string} encodedObjectName
* @param {string} upDomain
* @param {BlockStream} blkStream
* @param {FinishedEtags} finishedEtags
* @param {number} finishedBlock
* @param {number} totalBlockNum
* @param {PutExtra} putExtra
* @param {number} rsStreamLen
* @param {stream.Readable} rsStream
* @param {reqCallback} callbackFunc
*/
function resumeUploadV2 (

@@ -543,2 +680,13 @@ uploadToken,

/**
* @param {string} bucket
* @param {string} upDomain
* @param {string} uploadToken
* @param {string} encodedObjectName
* @param {Buffer | string} chunk
* @param {string} uploadId
* @param {number} partNumber
* @param {PutExtra} putExtra
* @param {reqCallback} callbackFunc
*/
function uploadPart (bucket, upDomain, uploadToken, encodedObjectName, chunk, uploadId, partNumber, putExtra, callbackFunc) {

@@ -552,18 +700,23 @@ const headers = {

'/' + partNumber.toString();
rpc.put(requestUrl, chunk, headers, function (err, ret, info) {
if (info.statusCode === 612) {
if (putExtra.resumeRecordFile) {
try {
fs.unlinkSync(putExtra.resumeRecordFile);
} catch (_e) {
// ignore
}
}
}
callbackFunc(err, ret, info);
});
rpc.put(requestUrl, chunk, headers, callbackFunc);
}
function completeParts (upDomain, bucket, encodedObjectName, uploadToken, finishedEtags,
putExtra, callbackFunc) {
/**
* @param {string} upDomain
* @param {string} bucket
* @param {string} encodedObjectName
* @param {string} uploadToken
* @param {FinishedEtags} finishedEtags
* @param {PutExtra} putExtra
* @param {reqCallback} callbackFunc
*/
function completeParts (
upDomain,
bucket,
encodedObjectName,
uploadToken,
finishedEtags,
putExtra,
callbackFunc
) {
const headers = {

@@ -585,16 +738,18 @@ Authorization: 'UpToken ' + uploadToken,

const requestBody = JSON.stringify(body);
rpc.post(requestUrl, requestBody, headers, function (err, ret, info) {
if (info.statusCode === 200 || info.statusCode === 612) {
if (putExtra.resumeRecordFile) {
try {
fs.unlinkSync(putExtra.resumeRecordFile);
} catch (_e) {
// ignore
}
}
}
callbackFunc(err, ret, info);
});
rpc.post(
requestUrl,
requestBody,
headers,
callbackFunc
);
}
/**
* @param {string} uploadToken
* @param {string | null} key
* @param {string} localFile
* @param {PutExtra} putExtra
* @param {reqCallback} callbackFunc
* @returns {Promise<UploadResult>}
*/
ResumeUploader.prototype.putFile = function (

@@ -607,10 +762,6 @@ uploadToken,

) {
const that = this;
const preferScheme = this.config.useHttpsDomain ? 'https' : 'http';
// PutExtra
putExtra = putExtra || new PutExtra();
const rsStream = fs.createReadStream(localFile, {
highWaterMark: conf.BLOCK_SIZE
});
const rsStreamLen = fs.statSync(localFile).size;
const isResumeUpload = putExtra.resumeRecordFile &&
fs.existsSync(putExtra.resumeRecordFile);
if (!putExtra.mimeType) {

@@ -624,36 +775,99 @@ putExtra.mimeType = mime.getType(localFile);

return this.putStream(uploadToken, key, rsStream, rsStreamLen, putExtra,
callbackWithRetryFunc);
putExtra = getDefaultPutExtra(
putExtra,
{
key
}
);
function callbackWithRetryFunc (err, ret, info) {
let needRetry = false;
if (putExtra.version === 'v1' &&
info.statusCode === 701 &&
isResumeUpload
) {
needRetry = true;
}
if (putExtra.version === 'v2' &&
info.statusCode === 612 &&
isResumeUpload
) {
needRetry = true;
}
if (needRetry) {
that.putFile(
// regions
return prepareRegionsProvider({
config: this.config,
bucketName: util.getBucketFromUptoken(uploadToken),
accessKey: util.getAKFromUptoken(uploadToken)
})
.then(regionsProvider => {
const resumeInfo = getResumeRecordInfo(putExtra.resumeRecordFile);
let preferredEndpointsProvider;
if (resumeInfo && Array.isArray(resumeInfo.upDomains)) {
preferredEndpointsProvider = new StaticEndpointsProvider(
resumeInfo.upDomains.map(d => new Endpoint(d, { defaultScheme: preferScheme }))
);
}
return doWorkWithRetry({
workFn: sendPutReq,
callbackFunc,
regionsProvider,
uploadApiVersion: putExtra.version,
// use resume upDomain firstly
preferredEndpointsProvider: preferredEndpointsProvider,
resumeRecordFilePath: putExtra.resumeRecordFile,
retryPolicies: this.retryPolicies
});
});
function sendPutReq (endpoint) {
endpoint = Object.create(endpoint);
endpoint.defaultScheme = preferScheme;
const rsStream = fs.createReadStream(localFile, {
highWaterMark: conf.BLOCK_SIZE
});
const rsStreamLen = fs.statSync(localFile).size;
return new Promise((resolve) => {
putReq(
endpoint,
uploadToken,
key,
localFile,
rsStream,
rsStreamLen,
putExtra,
callbackFunc
(err, ret, info) => {
destroy(rsStream);
resolve({ err, ret, info });
}
);
return;
}
callbackFunc(err, ret, info);
});
}
};
ResumeUploader.prototype.putFileWithoutKey = function (uploadToken, localFile,
putExtra, callbackFunc) {
/**
* @param {string} uploadToken
* @param {string} localFile
* @param {PutExtra} putExtra
* @param {reqCallback} callbackFunc
* @returns {Promise<UploadResult>}
*/
ResumeUploader.prototype.putFileWithoutKey = function (
uploadToken,
localFile,
putExtra,
callbackFunc
) {
return this.putFile(uploadToken, null, localFile, putExtra, callbackFunc);
};
/**
* @param {PutExtra} putExtra
* @param {Object} options
* @param {string | null} [options.key]
* @return {PutExtra}
*/
function getDefaultPutExtra (putExtra, options) {
options = options || {};
putExtra = putExtra || new PutExtra();
if (!putExtra.mimeType) {
putExtra.mimeType = 'application/octet-stream';
}
if (!putExtra.fname) {
putExtra.fname = options.key || '?';
}
if (!putExtra.version) {
putExtra.version = 'v1';
}
return putExtra;
}

@@ -5,3 +5,3 @@ # Qiniu Cloud SDK for Node.js

[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.md)
[![Build Status](https://api.travis-ci.org/qiniu/nodejs-sdk.svg?branch=master)](https://travis-ci.org/qiniu/nodejs-sdk)
[![NodeJS CI](https://github.com/qiniu/nodejs-sdk/actions/workflows/ci-test.yml/badge.svg?branch=master)](https://github.com/qiniu/nodejs-sdk/actions/workflows/ci-test.yml)
[![GitHub release](https://img.shields.io/github/v/tag/qiniu/nodejs-sdk.svg?label=release)](https://github.com/qiniu/nodejs-sdk/releases)

@@ -34,10 +34,10 @@ [![Code Climate](https://codeclimate.com/github/qiniu/nodejs-sdk.svg)](https://codeclimate.com/github/qiniu/nodejs-sdk)

参考文档:[七牛云存储 Node.js SDK 使用指南](http://developer.qiniu.com/kodo/sdk/nodejs)
参考文档:[七牛云存储 Node.js SDK 使用指南](http://developer.qiniu.com/kodo/sdk/nodejs)
## 测试
```
$ cd ./test/
$ source test-env.sh
```
$ cd ./test/
$ source test-env.sh
$ mocha --grep 'bucketinfo'
```
```

@@ -44,0 +44,0 @@ ## 贡献代码

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc