我有一个问题,我在将画布的imageData发送到服务器进行处理时遇到一些问题。
这是我的反应/还原操作,用于发送数据:
export const edit = (e) => dispatch => {
let payload = store.getState().image.imageData
payload = payload[payload.length-1]
console.log(payload)
const id = e.target.id ? e.target.id : e.target.parentElement.id
fetch(`/api/editing/${id}`, {
method: 'POST',
// headers: {
// "Content-Type": "application/json"
// },
body: JSON.stringify({ imgData: payload })
})
.then(res => res.json())
.then(json => {
dispatch({ type: UPDATE_IMAGE, payload: json.imgData })
// handle response display
})
}
这是我处理请求的方式:
const traitement = require('../../processing/traitement')
router.post('/:type', (req, res) => {
const { imgData } = req.body
const method = req.params.type
const imgDataProcessed = traitement[method](imgData)
return res.status(200).json({ imgData: imgDataProcessed })
})
下面是仿照方法的一个例子:
negatif: function(imgData) {
console.log(imgData)
var n = imgData.data.length;
for(i=0; i<n; i+=4){
imgData.data[i] = 255 - imgData.data[i];
imgData.data[i+1] = 255 - imgData.data[i+1];
imgData.data[i+2] = 255 - imgData.data[i+2];
imgData.data[i+3] = 255;
}
return imgData;
},
在我发送(我希望发送的内容)之前的console.log():
ImageData {data: Uint8ClampedArray(180000), width: 300, height: 150}
data: Uint8ClampedArray(180000) [0, 0, 255, 255, 0, 0, 255, 255, 0, 0,
255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255,
255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0,
0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255,
255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0,
0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255,
255, …]
height: 150
width: 300
__proto__: ImageData
我无法粘贴从服务器获得的内容,但是我丢失了宽度,高度键,typeof是Object而不是ImageData,数据键是typeof Object而不是Uint8ClampedArray。
所以我的问题是:如何使我的路线访问发送的相同数据,以便能够处理它?</ p>
您可以看到我以字符串化json的形式发送它们,并且我的服务器上有一个json bodyparser中间件,也许是从那里来的。 我也谈到了内容类型标题
编辑: 多亏了Kaiido,我已经修改了我的代码,使得它似乎只适用于1个示例
我如何修改我的代码,正面:
let payload = store.getState().image.imageData
payload = payload[payload.length-1]
const id = e.target.id ? e.target.id : e.target.parentElement.id;
console.log(payload)
const meta = {
width: payload.width,
height: payload.height
}
const formData = new FormData();
formData.append("meta", JSON.stringify(meta));
formData.append("data", new Blob([payload.data.buffer]))
fetch(`/api/editing/${id}`, {
method: "POST",
body: formData
})
.then(res => res.arrayBuffer())
.then(buffer => {
console.log(buffer)
const data = new Uint8ClampedArray(buffer)
console.log(data.length)
const newImg = new ImageData(data, payload.width, payload.height)
return newImg
})
后退:
router.post('/:type', (req, res) => {
let form = new formidable.IncomingForm()
form.parse(req, (err, fields, files) => {
fs.readFile(files.data.path, (err, data) => {
const imgProcessed = traitement[req.params.type](data)
console.log(imgProcessed.length)
return res.status(200).json([imgProcessed])
})
})
})
还有1个问题:假设要进行测试,我使用的是150 * 300px的图像,因此我的数据数组的长度应为180000(300 * 150 * 4),直到我发送服务器的响应为止。
当前台收到响应时,它将调用res.arrayBuffer()
然后创建一个新的Uint8ClampedArray,但是在这种情况下,我的长度不再是180000,而是543810。
正如Kaiido所说,我可能想切片该数组,直到我尝试过,但没有用。
我应该怎么切呢? 180000个第一? 180000个?其他方式吗?
答案 0 :(得分:1)
做
const { imgData } = JSON.parse(req.body)
这是JSON.stringify
的反义,当您通过stringify发送数据但在途中不将其解析回原始对象时需要使用。
答案 1 :(得分:1)
JSON仅处理很少的类型:字符串,数字,数组,对象,布尔值和null。
没有枚举属性的ImageData将 JSON.stringified 变为"{}"
(一个空对象)。
const img = new ImageData(6, 6);
img.data.fill(0xFF);
console.log('JSON', JSON.stringify(img)); // "{}"
const props = Object.getOwnPropertyDescriptors(img);
console.log('own properties', props); // {}
您将放弃所有信息。
因此,为了避免这种情况,您必须在请求中手动发送所有这些信息。
只需将所需的值移动到另一个对象,听起来很容易:
const img = new ImageData(6,6);
const dataholder = (({width, height, data}) => ({width, height, data}))(img);
console.log(JSON.stringify(dataholder));
但是,您会注意到data
属性没有以适当的方式进行字符串化。
实际上是将 JSON.stringified 转换为简单对象并将其所有索引都设置为键的TypedArray。 现在只需要144个字节来表示6px * 6px图像数据的内容现在就需要1331个字节。
const typedArray = new Uint8ClampedArray(6*6*4).fill(0xFF);
console.log(JSON.stringify(typedArray));
const as_string = new Blob([JSON.stringify(typedArray)]);
const as_arraybuffer = new Blob([typedArray]);
console.log('as string', as_string.size, 'bytes');
console.log('as arraybuffer', as_arraybuffer.size, 'bytes');
我会让您为更大的图像做数学运算,但是您可能会考虑其他方法...
您最好发送其底层的ArrayBuffer,而不是发送此Uint8ClampedArray的字符串化版本。这听起来可能需要编写更多代码,但可以节省带宽,甚至树木。
因此,要将您的ImageData发送到服务器,您会这样做
const img = new ImageData(6, 6);
img.data.fill(0xFF);
const meta = {
width: img.width,
height: img.height
// add more metadata here if needed
};
const formdata = new FormData();
formdata.append("meta", JSON.stringify( meta ))
formdata.append("data", new Blob([img.data.buffer]));
console.log(...formdata); // [key, value], [key, value]
// here data is a File object and weights only 144 bytes
/* // send the formdata to your server
fetch('yoururl', {
method: 'POST',
body: formdata
})
.then(response => response.arrayBuffer())
... // we'll come to it later
*/
现在,在服务器上,要检索请求的所有部分,您将拥有to grab the form-data content,并以文件上载的方式访问图像的数据。
然后,您必须读取此上传文件(例如,使用fs.readFile(temp_path)
并直接从返回的Buffer中进行操作(自Buffers in node are Uint8Array起)。
操作完后,您可以直接发送修改后的Buffer作为响应。
现在,您只需要求将Response作为ArrayBuffer使用,并从该ArrayBuffer生成新的ImageData:
//... see above
fetch('yoururl', {
method: 'POST',
body: formdata
})
// request as ArrayBuffer
.then(response => response.arrayBuffer())
.then(buffer => {
// create a new View over our ArrayBuffer
const data = new Uint8ClampedArray(buffer);
const new_img = new ImageData(data, img.width, img.height);
return new_img;
});
这里假设您的前台不需要服务器响应中的元数据(因为您已经拥有了)。
如果您需要一些元数据,则可以在ArrayBuffer响应的开头添加前缀(分配一个插槽以告知其大小,然后在其中接收