Getting PDF as blob in server components but cannot send it to client to be downloaded
Answered
Genio posted this in #help-forum
GenioOP
I always face this error
my server component
Note, i solved it now by converting it to base64 then back again to file as client component but i was wondering if there was a better approuch. I cannot use fetch in client due to cors policy
client
Blob { size: 4130863, type: 'application/pdf' }
⨯ Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
at stringify (<anonymous>)my server component
Note, i solved it now by converting it to base64 then back again to file as client component but i was wondering if there was a better approuch. I cannot use fetch in client due to cors policy
"use server"
const getOrderInvoice = async (orderId: string) => {
const url = new URL(
`${baseUrl}/service-requests/${orderId}/export`
);
const session = await getServerSession(authOptions);
console.log(url.toString());
try {
const response = await fetch(url.toString(), {
method: 'GET',
headers: {
authorization: `Bearer ${session?.accessToken}`
}
});
const data = await response.arrayBuffer();
const b64 = Buffer.from(data).toString('base64');
if (!response.ok) {
throw new Error('Cannot get order invoice');
}
return b64;
} catch (error: any) {
//return { error };
}
};client
"use client"
const downloadPDF = (pdf: string) => {
const linkSource = `data:application/pdf;base64,${pdf}`;
const downloadLink = document.createElement('a');
const fileName = 'abc.pdf';
downloadLink.href = linkSource;
downloadLink.download = fileName;
downloadLink.click();
};
----
const downloadInvoice = async () => {
//const {blob,error} =
const base64 = await getOrderInvoice(seeker?.id);
downloadPDF(base64 as string);
};
console.log(seeker);
return (
<Button onClick={downloadInvoice}>
Download Invoice
</Button>Answered by Ray
in this case, I would use route handler over server action
// download/[orderId]/route.ts
export const dynamic = "force-dynamic";
export function GET(req: Request, { params }) {
const url = new URL(`${baseUrl}/service-requests/${params.orderId}/export`);
return fetch(url.toString(), {
method: "GET",
headers: {
authorization: `Bearer ${session?.accessToken}`,
},
});
}
// client.tsx
<a href={`/download/${orderId}`} download>2 Replies
@Genio I always face this error
ts
Blob { size: 4130863, type: 'application/pdf' }
⨯ Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
at stringify (<anonymous>)
my server component
**Note, i solved it now by converting it to base64 then back again to file as client component but i was wondering if there was a better approuch. I cannot use fetch in client due to cors policy**
ts
"use server"
const getOrderInvoice = async (orderId: string) => {
const url = new URL(
`${baseUrl}/service-requests/${orderId}/export`
);
const session = await getServerSession(authOptions);
console.log(url.toString());
try {
const response = await fetch(url.toString(), {
method: 'GET',
headers: {
authorization: `Bearer ${session?.accessToken}`
}
});
const data = await response.arrayBuffer();
const b64 = Buffer.from(data).toString('base64');
if (!response.ok) {
throw new Error('Cannot get order invoice');
}
return b64;
} catch (error: any) {
//return { error };
}
};
client
tsx
"use client"
const downloadPDF = (pdf: string) => {
const linkSource = `data:application/pdf;base64,${pdf}`;
const downloadLink = document.createElement('a');
const fileName = 'abc.pdf';
downloadLink.href = linkSource;
downloadLink.download = fileName;
downloadLink.click();
};
----
const downloadInvoice = async () => {
//const {blob,error} =
const base64 = await getOrderInvoice(seeker?.id);
downloadPDF(base64 as string);
};
console.log(seeker);
return (
<Button onClick={downloadInvoice}>
Download Invoice
</Button>
in this case, I would use route handler over server action
// download/[orderId]/route.ts
export const dynamic = "force-dynamic";
export function GET(req: Request, { params }) {
const url = new URL(`${baseUrl}/service-requests/${params.orderId}/export`);
return fetch(url.toString(), {
method: "GET",
headers: {
authorization: `Bearer ${session?.accessToken}`,
},
});
}
// client.tsx
<a href={`/download/${orderId}`} download>Answer
@Ray in this case, I would use route handler over server action
tsx
// download/[orderId]/route.ts
export const dynamic = "force-dynamic";
export function GET(req: Request, { params }) {
const url = new URL(`${baseUrl}/service-requests/${params.orderId}/export`);
return fetch(url.toString(), {
method: "GET",
headers: {
authorization: `Bearer ${session?.accessToken}`,
},
});
}
// client.tsx
<a href={`/download/${orderId}`} download>
GenioOP
Thank you so much, will try it