目标
调接口传 FormData,返回的 Blob 转 url 用 <a> 下载。
FormData
Object->[Key, Value][]->FormDataR.reduce()擅长数组百变reducer()使累加值,即FormData实例添加键值对,再返回累加值initialValue为新创建的FormData实例
- 为确保每次调用
toFormData()都会创建新的FormData实例,这次就用了R.constructN(0, FormData)
reducer() 最初这么写
(formData, pair) => (formData.append(...pair), formData);
后来经过一番演变
R.tap()避免再写一次返回formDataR.apply()避免写...R.bind()改变this指向
/** (FormData, [Key, Value]) -> FormData */
const Append = (formData, pair) => R.tap(
R.compose( R.apply(R.__, pair), R.bind(FormData.prototype.append) ),
formData
);
/** Object -> FormData */
const toFormData = R.compose(
R.converge( R.reduce(Append), [R.constructN(0, FormData), Object.entries] ),
R.map(R.when( R.isNil, R.always('') ))
);
FormData 再变回 Object 也一样
console.log(
Object.fromEntries( toFormData({ name: 'Tom' }) )
);
// { name: "Tom" }
文件名
调接口后,开发工具看 Response Headers,文件名在 Content-Disposition,用于 <a> download 赋值
| field | value |
|---|---|
| Access-Control-Allow-Origin | * |
| Connection | close |
| Content-Disposition | attachment; filename="20240506135858.zip" |
| Content-Length | 1992 |
| Content-Type | application/zip |
| Date | Mon, 06 May 2024 05:58:58 GMT |
方案一 R.slice()
/** string -> string -> number */
const StartAt = search => R.compose( R.add(search.length), R.indexOf(search) );
/** Headers -> string */
const Filename = R.compose(
R.converge( R.slice(R.__, -1, R.__), [StartAt('filename="'), R.identity] ),
R.prop('content-disposition'),
Object.fromEntries
);
方案二 R.match()
/** Headers -> string */
const Filename = R.compose(
R.head,
R.match(/\d+\.zip/), // ["20240506135858.zip", "groups": undefined, "index": 22, "input": "attachment; filename=\"20240506135858.zip\""]
R.prop('content-disposition'),
Object.fromEntries
);
完整代码
fetch('/api/example', {
headers: {
accesstoken: /* accesstoken */,
},
method: 'POST',
body: toFormData(/* Object */),
})
.then(R.invoker(0, 'blob'))
.then(blob => {
const url = URL.createObjectURL(blob);
const aElem = document.createElement('a');
aElem.href = url;
aElem.download = Filename(response.headers);
aElem.click();
URL.revokeObjectURL(url);
});