I am trying to do a POST request to download a pdf file from the sever. The error I get is message: "Http failure during parsing". I'm not sure why it would work for a GET request but not for a POST request or I am missing something.
this.httpClient.post(url, {
responseType: 'blob' as 'json',
observe: 'response'
})
.pipe(take(1))
.subscribe((data:HttpResponse<Blob>) => {
const blob = new Blob([data.body], { type: 'application/pdf' });
saveAs(blob, 'test.pdf');
});
However if I do a GET request, it seems to work.
this.httpClient.get<Blob>(url,
{
observe: 'response',
responseType: (('blob') as any) as 'json'
})
.pipe(take(1))
.subscribe(
(response: HttpResponse<Blob>) => {
const blob = new Blob([response.body], { type: mediaType });
saveAs(blob, 'Report.pdf');
},
(error: any) => {
console.error(error);
}
);
Server
[HttpPost]
[Route("download/{id}")]
public IHttpActionResult DownloadPdf()
{
var buffer = _test.RenderDrawing();
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(buffer)
};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = $"Test.pdf"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return ResponseMessage(result);
}
In you post request, you are defining types differently. try something like this:
this.httpClient.post(url, {
responseType: 'blob',
observe: 'response'
})
.pipe(take(1))
.subscribe((data:HttpResponse<Blob>) => {
const blob = new Blob([data.body], { type: 'application/pdf' });
saveAs(blob, 'test.pdf');
});
Related
I am requesting a file stored in MongoDB using GridFS, I am using gridfs-stream to read the file.
The server-side code:
exports.getFile = catchAsync(async (req, res, next) => {
gfs.files.findOne({ filename: req.params.filename }, (err, file) => {
if (err)
return res.status(404).json({
status: 'error',
message: 'Something went wrong',
});
if (!file || file.length === 0) {
return res.status(404).json({
status: 'error',
message: 'File not found',
});
}
res.set({
'Content-Type': file.mimeType,
'Content-Disposition': 'attachment; filename=' + file.filename,
});
const readStream = gridfsBucket.openDownloadStream(file._id);
readStream.on('error', function (err) {
res.end();
});
readStream.pipe(res);
});
});
On the client-side, I am using Axios to request the file using the GET method:
Client-side code:
export const downloadFile = (filename) => {
return (dispatch) => {
api.get(`/api/v1/chats/file/${filename}`).then((res) => {
console.log(res.data);
});
};
};
The data I receive for an image looks like this:
enter image description here
Now all I want is to store the file on the disk.
This is my session save and fetch method:
[HttpPost]
[Route("save-to-session")]
public IActionResult SaveToSession(string id)
{
HttpContext.Session.SetString("user", id);
return Content(id);
}
[HttpGet]
[Route("fetch-from-session")]
public IActionResult FetchFromSession()
{
string name = HttpContext.Session.GetString("user");
return Content(name);
}
I'm using Sql Server Cache:
services.AddDistributedSqlServerCache(options => {
options.ConnectionString = Configuration.GetConnectionString("TestMEApiContext");
options.SchemaName = "dbo";
options.TableName = "Session";
});
services.AddSession(options => {
options.Cookie.Name = ".Session";
options.IdleTimeout = TimeSpan.FromMinutes(3000);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
and the data is saved to the database but when i try to fetch it always returns null.
This is how i try to fetch it in my react code:
fetch(`https://localhost:44369/api/users/fetch-from-session`, { method: 'GET', credentials: 'include', mode: 'cors' }).then(function (body) {
return body.text();
}).then((response) => {
console.log("My response")
console.log(response) // <-THIS IS NULL
});
And this is how i try to save it but save works:
await this.getUserId().then(async function (userId) {
fetch(`https://localhost:44369/api/users/save-to-session?id=${userId}`, {
method: 'POST',
mode: 'cors',
credentials: 'include'
});
})
Actually we have site which use dropzone.js for uploading image to the server.
I'm working with react native app and need to replicate dropzone js behavior.
I'm trying to send base64 image but request return Multipart requests must contain at least one part.
saveImages(images) {
let config = {
headers: {
'Content-Type': 'multipart/form-data',
'X-Requested-With': 'XMLHttpRequest'
}
};
images.forEach(img => {
let imgFormData = `data:${img.mime};base64,${(img.data)}`;
let blob = this.dataURItoBlob(imgFormData);
let formData = new FormData(document.forms[0]);
formData.append('file', blob);
axios.post(SAVE_IMAGE_URL, formData, config)
.then(res => console.log(res))
.catch(err => {
console.log(err);
console.log(err.status);
console.log(err.code);
})
});
}
dataURItoBlob = (dataURI) => {
let byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
let mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
let ia = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
Fixed with react-native-fetch-blob package
images.forEach(img => {
RNFetchBlob.fetch('POST', SAVE_IMAGE_URL, {
'Content-Type': 'multipart/form-data'
}, [
// element with property `filename` will be transformed into `file` in form data
{name: 'file', filename: 'file.png', data: img.data}
]).then((resp) => {
console.log(resp);
// ...
}).catch((err) => {
// ...
})
})
I think my solution is in this question but I can't get it to work Promise.all behavior with RxJS Observables?
I'm trying to return an observable on two promises via forkJoin.
One promise gets an ID from the server and another processes a file to generate a thumbnail.
export function createSceneFromFile(action$) {
return action$.ofType(CREATE_SCENE_FROM_FILE)
.mergeMap(({locationId,file}) =>
createSceneThumb(locationId,file)
.map((res,preview) => {
console.log(res,preview)
if (res.error) {
return { type: CREATE_SCENE_FAILED, payload: res.message }
} else {
return {type: CREATE_SCENE_SUCCESS, payload: {...res.payload,preview} }
}
})
.catch(err => { return { type: CREATE_SCENE_FAILED, message: err } })
)
}
function createSceneThumb(locationId,file){
const request = fetch(`${API_URL}/location/${locationId}/createscene/${file.name}/`, {
method: 'get',
credentials: 'include',
}).then(res => res.json())
const thumb = fileToScenePreview(file)
return Observable.forkJoin(request,thumb)
}
export function fileToScenePreview(file){
return new Promise((resolve,reject)=>{
getFileThumb(file).then((canvas)=> {
canvas.toBlob((blob) => {
blob.lastModifiedDate = new Date()
blob.name = 'scenepreview_' + file.name + '.png'
const uploader = new S3Upload({
getSignedUrl: getSignedUrl,
uploadRequestHeaders: {'x-amz-acl': 'public-read'},
contentType: 'image/png',
scrubFilename: (filename) => filename.replace(/[^\w\d_\-.]+/ig, ''),
contentDisposition: 'auto',
s3path: 'assets/',
onError:()=>reject,
onFinishS3Put: ()=>resolve(blob.name),
})
uploader.uploadFile(blob)
})
})
})
}
But i never get a response.
Is this the right way of going about it?
Suppose I have an API that return user detail:
/api/get_user/1
{
"status": 200,
"data": {
"username": "username1",
"email": "username#email.com"
}
}
And a "main function" like this:
function main (sources) {
const request$ = sources.ACTIONS
.filter(action => action.type === 'GET_USER_REQUEST')
.map(action => action.payload)
.map(payload => ({
category: 'GET_USER_REQUEST',
url: `${BASE_URL}/api/get_user/${payload.userId}`,
method: 'GET'
}))
const action$ = sources.HTTP
.select('GET_USER_REQUEST')
.flatten()
.map(response => response.data)
const sinks = {
HTTP: request$,
LOG: action$
}
return sinks
}
For testing the "ACTION" source, I can simply made an xstream observable
test.cb('Test main function', t => {
const actionStream$ = xs.of({
type: 'GET_USER_REQUEST',
payload: { userId: 1 }
})
const sources = { ACTION: actionStream$ }
const expectedResult = {
category: 'GET_USER_REQUEST',
url: `${BASE_URL}/api/get_user/${payload.userId}`,
method: 'GET'
}
main(sources).HTTP.addEventListener({
next: (data) => {
t.deepEqual(data, expectedResult)
},
error: (error) => {
t.fail(error)
},
complete: () => {
t.end()
}
})
})
The question is. Is it possible to do the same thing (using plan xstream observable)
to test cycle-http driver without a helper from something like nock?
Or is there a better way to test something like this?
You can mock out the HTTP source like so:
test.cb('Test main function', t => {
const actionStream$ = xs.of({
type: 'GET_USER_REQUEST',
payload: { userId: 1 }
})
const response$ = xs.of({
data: {
status: 200,
data: {
username: "username1",
email: "username#email.com"
}
}
});
const HTTP = {
select (category) {
// if you have multiple categories you could return different streams depending on the category
return xs.of(response$);
}
}
const sources = { ACTION: actionStream$, HTTP }
const expectedResult = {
category: 'GET_USER_REQUEST',
url: `${BASE_URL}/api/get_user/${payload.userId}`,
method: 'GET'
}
main(sources).HTTP.addEventListener({
next: (data) => {
t.deepEqual(data, expectedResult)
},
error: (error) => {
t.fail(error)
},
complete: () => {
t.end()
}
})
})
Really, we should have a mockHTTPSource helper to make this a bit easier. I have opened an issue to that effect. https://github.com/cyclejs/cyclejs/issues/567
If you want to test that certain things happen at the correct time, you could use this pattern in conjunction with #cycle/time.
http://github.com/cyclejs/time