前言
上传文件是我们最常用的方法之一了,几乎每个项目里都会使用到,实现起来也并不复杂,今天我们就简单写一个上传方法,并且支持进度显示和取消上传功能。
方法实现
export const uploadFile = (file, options) => {
const {
url = VITE_GLOB_UPLOAD_URL,
onProgress,
showToastFn = showLoading,
signal
} = Object.assign({}, options);
const hide = showToastFn && showToastFn();
const formData = new FormData();
formData.append('file', file);
if (options && options.body) {
Object.keys(options.body).forEach(key => {
formData.append(key, options.body[key]);
});
}
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url);
// 设置请求头
xhr.setRequestHeader('Authorization', getToken());
xhr.setRequestHeader('appCode', 'dsops');
xhr.setRequestHeader('tenantId', tenantStore.getTenantVal);
xhr.setRequestHeader('Org-Type', tenantStore.getOrgType);
xhr.setRequestHeader('Org-Id', tenantStore.getOrgId);
// 监听取消请求的信号
if (signal) {
signal.addEventListener('abort', () => {
console.log('abort');
xhr.abort();
});
}
xhr.onload = () => {
hide && hide(true);
const response = JSON.parse(xhr.responseText);
if (xhr.status === 200) {
resolve(response.data);
} else {
reject(response);
}
};
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
const percentComplete = Math.floor((event.loaded / event.total) * 100);
onProgress && onProgress(percentComplete);
}
};
xhr.onerror = () => {
hide && hide(false);
reject(xhr.statusText);
};
xhr.onabort = () => {
hide && hide(false);
reject('请求已被取消');
};
xhr.send(formData);
});
};
export const createUploadController = () => {
const controller = new AbortController();
return {
signal: controller.signal,
abort: () => controller.abort(),
};
};
函数参数
file
: 上传的文件对象,这是通过<input type="file">
或其他方式获取的文件。options
: 一个对象,包含一些可选配置,如上传的 URL、进度回调、信号控制、额外数据等。url
: 上传的目标 URL,默认为VITE_GLOB_UPLOAD_URL
。onProgress
: 上传进度的回调函数。showToastFn
: 显示加载中的提示函数,默认使用showLoading
函数。signal
: 用于控制上传请求取消的信号。
使用 Object.assign
合并默认配置和传入的 options
对象。
构建 FormData
对象
const formData = new FormData();
formData.append('file', file);
if (options && options.body) {
Object.keys(options.body).forEach(key => {
formData.append(key, options.body[key]);
});
}
使用 FormData
对象来构建上传的数据,首先将文件附加到 formData
,如果 options
中包含其他数据(body
),则将其附加到 formData
中。
创建并发送 XMLHttpRequest
创建 XMLHttpRequest
对象
const xhr = new XMLHttpRequest();
xhr.open('POST', url);
创建 XMLHttpRequest
对象,并使用 POST
方法打开连接到指定的 url
。
设置请求头
xhr.setRequestHeader('Authorization', getToken());
xhr.setRequestHeader('appCode', 'dsops');
xhr.setRequestHeader('tenantId', tenantStore.getTenantVal);
xhr.setRequestHeader('Org-Type', tenantStore.getOrgType);
xhr.setRequestHeader('Org-Id', tenantStore.getOrgId);
通过 setRequestHeader
方法设置请求头,包括认证信息、应用代码、租户信息等。这些信息通常用于服务器端验证和处理请求。这些信息应该根据实际情况定义。
请求取消信号监听
if (signal) {
signal.addEventListener('abort', () => {
console.log('abort');
xhr.abort();
});
}
如果传入了 signal
参数,则为其添加一个监听器,在 signal
触发 abort
事件时,中止上传请求。
请求成功的回调
xhr.onload = () => {
hide && hide(true);
const response = JSON.parse(xhr.responseText);
if (xhr.status === 200) {
resolve(response.data);
} else {
reject(response);
}
};
当上传完成时,onload
回调被触发。首先,隐藏加载提示,然后解析服务器返回的响应内容。
- 状态码为200:表示请求成功,解析并返回上传结果。
- 其他状态码:将服务器的响应作为错误处理。
上传进度的回调
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
const percentComplete = Math.floor((event.loaded / event.total) * 100);
onProgress && onProgress(percentComplete);
}
};
当上传过程中触发 onprogress
事件时,计算并更新上传进度(以百分比表示)。如果提供了 onProgress
回调函数,则将进度百分比传递给该回调。
请求错误的回调
xhr.onerror = () => {
hide && hide(false);
reject(xhr.statusText);
};
如果请求出现错误(例如网络问题),触发 onerror
回调,隐藏加载提示,并返回错误信息。
请求被取消的回调
xhr.onabort = () => {
hide && hide(false);
reject('请求已被取消');
};
当请求被取消时,触发 onabort
回调,隐藏加载提示,并返回取消请求的错误信息。
发送请求
xhr.send(formData);
最后,将 formData
发送到服务器,开始上传文件。
createUploadController
函数
createUploadController
函数用于创建一个控制上传请求的对象。该对象包含两个属性:signal
和 abort
。
export const createUploadController = () => {
const controller = new AbortController();
return {
signal: controller.signal,
abort: () => controller.abort(),
};
};
signal
: 用于监听和控制请求取消的信号。abort
: 取消上传请求的方法。当调用abort
方法时,触发关联的signal
的abort
事件,中止正在进行的上传。
总结
文件上传进度显示功能,是通过监听xhr的upload.onprogress事件实现的;中断文件上传是通过原生提供的AbortController
对象实现的;其实都比较简单,主要是是否了解过相关接口,如果了解的话,还是十分简单的。