Problem to generate pdf from a blob in an expo app using FileSystem - react-native

I get a blob and I treat it like this:
const file = response.data;
var blob = new Blob([file], {
type: 'application/pdf',
});
const fileReaderInstance = new FileReader();
fileReaderInstance.readAsDataURL(blob);
fileReaderInstance.onload = async () => {
const fileUri = `${FileSystem.documentDirectory}file.pdf`;
await FileSystem.writeAsStringAsync(
fileUri,
fileReaderInstance.result.split(',')[1],
{
encoding: FileSystem.EncodingType.Base64,
}
);
console.log(fileUri);
Sharing.shareAsync(fileUri);
};
however when I generate and share the file, I can't access it and if I get its URI and search on the web it returns:

i solved my problem in this way:
This is a func who get other data to request, do the request (generate PDF()) and treat the data and generate by received blob the buffer on (fileReaderInstance.result) who is shared in Sharing.shareAsync()
const generatePDF = async () => {
setAnimating(true);
const companyReponse = await CompanyService.getCompany();
const peopleResponse = await PeopleService.getPerson(sale.customerId);
const company = companyReponse.response.company;
const people = peopleResponse.response;
const quote = false;
const json = await SaleService.generatePDF({
sale,
company,
people,
quote,
});
if (json && json.success) {
try {
const fileReaderInstance = new FileReader();
fileReaderInstance.readAsDataURL(json.data);
fileReaderInstance.onload = async () => {
const base64data = fileReaderInstance.result.split(',');
const pdfBuffer = base64data[1];
const path = `${FileSystem.documentDirectory}/${sale._id}.pdf`;
await FileSystem.writeAsStringAsync(`${path}`, pdfBuffer, {
encoding: FileSystem.EncodingType.Base64,
});
await Sharing.shareAsync(path, { mimeType: 'application/pdf' });
};
} catch (error) {
Alert.alert('Erro ao gerar o PDF', error.message);
}
}
setAnimating(false);
}
This is the func in SaleServicegeneratePDF who do the request to api and pass the parameters that return a blob of pdf using axios:
generatePDF: async ({ sale, company, people, quote }) => {
const token = await AsyncStorage.getItem('token');
const body = { sale, company, people, quote };
try {
const response = await axios(`${BASE_API}/generate-sale-pdf`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: token,
},
responseType: 'blob',
data: body,
});
return {
success: true,
data: response.data,
};
} catch (err) {
return err.error;
}
},

I have solved this problem by passing the blob string to WriteAsStringAsync method of FileSystem library from expo.
const blobDat = data.data[0].data; //blob data coming from an API call
const fileUri = FileSystem.documentDirectory + `testt.pdf`; //Directory Link of the file to be saved
await FileSystem.writeAsStringAsync(fileUri, blobDat, {
encoding: FileSystem.EncodingType.UTF8,
}) //This step writes the blob string to the pdf fileURI
await IntentLauncher.startActivityAsync("android.intent.action.VIEW", {
data: fileUri,
flags: 1,
type: "application/pdf",
});
//prompts user with available application to open the above created pdf.

Related

uploading files to endpoint from a static webpage

I am trying to upload files to an S3 endpoint from a static HTML page but the files are always malformed when I download them from the bucket. The relevant code is below - what am I doing wrong with fetch?
const onSubmitForm = function (e) {
const file = this.files[0];
const reader = new FileReader();
// reader.readAsText(file); // didn't work
reader.readAsDataURL(file); // also didn't work
reader.onload = async function () {
const bodyData = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
file: {
// the backend endpoint expects a base64 encoded img
// the upload completes but the
"data": reader.result.toString()
},
"name": file.name
})
}
const response = await fetch(uploadUrl, bodyData).then(
res => res.json()
);
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
//
}

How to pass a file as a graphql variable?

How do I pass a file upload as a graphql mutation variable? Submitting this test gives me a 400 bad request with this error:
Variable \"$thumbnailFile\" got invalid value {}; Upload value invalid.
I am using graphql-upload. I am sending my queries like this:
async function graphQLFetch(query, variables = {}){
const response = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables })
});
const body = await response.text();
const result = JSON.parse(body);
return result.data;
}
In my react component:
handleThumbnailChange(event){
const thumbnailFile = event.target.files[0];
const query = `mutation addThumbnailTest($thumbnailFile: Upload){
addThumbnailTest(thumbnailFile: $thumbnailFile){
thumbnailSrc
}
}`;
const data = await graphQLFetch(query, {
thumbnailFile
});
}
My schema:
scalar Upload
type Mutation {
addThumbnailTest(thumbnailFile: Upload): String
}
My server is setup like the following:
const { ApolloServer } = require('apollo-server-express');
const { GraphQLUpload, graphqlUploadExpress } = require('graphql-upload');
const express = require('express');
const resolvers = {
Upload: GraphQLUpload,
Mutation: {
handleThumbnailChange: handleThumbnailChange,
}
}
const server = new ApolloServer({
typeDefs: fs.readFileSync(path.join(__dirname, 'schema.graphql'), 'utf-8'),
resolvers
});
async function installHandler(app){
await server.start();
server.applyMiddleware({ app, path: '/graphql' });
}
const app = express();
(async function start(){
try{
// Install GraphQL API Handler
app.use(graphqlUploadExpress());
await installHandler(app);
I had to submit the query as form data and without the { 'Content-Type': 'application/json' } header
async function graphQLFetch(query, variables = {}, multipart = false){
let request;
if(multipart){
const data = {
operations: JSON.stringify({
query,
variables: {
...variables,
file: null
}
}),
map: JSON.stringify({
'0': [
'variables.file'
]
})
};
const requestBody = new FormData();
for(const name in data) {
requestBody.append(name, data[name]);
}
requestBody.append('0', variables.file);
request = {
method: 'POST',
body: requestBody
}
} else {
request = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables })
}
}
const response = await fetch('/graphql', request);
const responseBody = await response.text();
const result = JSON.parse(responseBody);
return result.data;
}

How to send `application/zip` in react-native

I want to generate a zip file and then POST it up. I am trying to leverage this lib JSZip to create a zip with several files in it. Here is what I have right now.
handlePendingItems = async (routeId: number) => {
try {
await dbClient.addLogLine(`Need to delete photos.`);
const [debugLogs, pendingTasks, pendingUploads] = await this.getPendingItems();
const dbVersion = await dbClient.getUserSchemaVersion();
let appInfo = `Database Version = ${dbVersion}\n`;
appInfo += `App Version = ${version}`;
const zip = new JSZip();
zip.file('app-info.txt', appInfo);
if (debugLogs.length) {
const debugLogsCsv = convertDataToCSV(debugLogs);
zip.file('debug-logs.csv', debugLogsCsv);
}
if (pendingTasks.length) {
const pendingTasksCsv = convertDataToCSV(pendingTasks);
zip.file('pending-tasks.csv', pendingTasksCsv);
}
if (pendingUploads.length) {
const pendingUploadsCsv = convertDataToCSV(pendingUploads);
zip.file('pending-uploads.csv', pendingUploadsCsv);
}
const test = await zip.generateAsync({ type: 'binarystring' });
console.log(test);
const res = PhotoService.uploadLogs(routeId, test);
// const zipFile = await zip.generateAsync({
// type: 'blob',
// compression: 'DEFLATE',
// compressionOptions: {
// level: 9
// }
// });
} catch (error) {
console.log(error);
}
};
I can't use the type: 'blob' in react-native, it throws an error as unsupported on this platform. That being said I am trying to use other types to pass to react-native-fetch-blob. To be posted via the following code.
uploadLogs(driverRouteId: number, logs: string) {
const store = getStore();
const token = store.getState()?.auth?.token || '';
return new Promise((resolve, reject) => {
RNFetchBlob.fetch(
'POST',
`${API_HOST}driver/route/${driverRouteId}/logs`,
{
'Content-Type': 'application/zip',
Authorization: `Bearer ${token}`
},
logs
).then(
(res) => {
console.log(res);
resolve(res);
},
(err) => {
console.log(err);
debugger;
reject(err);
}
);
});
}
I read that fetch blob will attempt to parse base64 string if the content-encoding is a specific type which in this case has to be application/zip (so no parsing in this case) so I am trying to call generateAsync with type string however, the zip the file the server stores is in an unsupported format. How can I generate the zip file containing the three csv files and the txt file and send it using either fetch blob or just plain axios in react-native.
Figured out a solution using a different library to zip.
npm install react-native-zip-archive --save
npm install react-native-fs --save
npm install rn-fetch-blob --save
import RNFetchBlob from 'rn-fetch-blob';
import { zip } from 'react-native-zip-archive';
import RNFS from 'react-native-fs';
const pathsToZip = [];
const zipPath = `${RNFS.DocumentDirectoryPath}/test.zip`;
const appInfo = 'App info string to put in txt file'
const appInfoPath = RNFS.DocumentDirectoryPath + '/appInfo.txt';
await RNFS.writeFile(appInfoPath, appInfo, 'utf8');
pathsToZip.push(appInfoPath);
await zip(pathsToZip, zipPath);
await uploadLogs(zipPath) {
return new Promise((resolve, reject) => {
RNFetchBlob.fetch(
'POST',
`postpath`,
{
'Content-Type': 'application/zip',
},
RNFetchBlob.wrap(zipPath)
).then(
(res) => {
console.log(res);
resolve(res);
},
(err) => {
console.log(err);
debugger;
reject(err);
}
);
});
}

not playing audio from buffer stored in the database

I've spent 2 days with this thing and couldn't find a solution but seems like im getting closer. The goal is to record audio in the browser and store in the the database and get it whenever i need it.
I encoded the audio to base64 and sent it to the server stored as binary in the mongodb, created a get route in which i find the audio by id and send the buffer via res.send() along with the content type set to audio/webm (default mime type)
but the thing is I'm getting this blank video, seems like it's not knowing how to decode it or something. There might be something wrong with the content type.
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then((mediaRecorderObj) => {
let mediaRecorder = new MediaRecorder(mediaRecorderObj, { mimType: 'audio/webm;base64' })
const m = mediaRecorder;
let chunk = []
startBtn.addEventListener('click', () => {
mediaRecorder.start()
console.log('started recording..');
})
endBtn.addEventListener('click', () => {
mediaRecorder.stop()
console.log('just stopped recording');
})
//-----When Data Is Available
mediaRecorder.ondataavailable = (e) => {
chunk.push(e.data);
}
//--------When finished adding it to chunk
mediaRecorder.onstop = () => {
const blob = new Blob(chunk, { 'type': 'audio/webm' })
chunk = []
const reader = new FileReader()
reader.readAsDataURL(blob)
reader.onloadend = async () => {
const buffer = reader.result
const bodyObj = {
voice: buffer
}
await fetch('http://localhost:3000/upload-spoken-bio', {
method: 'post',
body: JSON.stringify(bodyObj),
headers: { 'Content-Type': 'application/json' }
})
}
}
})
and this is the server side
//----------Upload
spokenBio.post('/upload-spoken-bio', async (req, res) => {
const buffer = req.body
try {
const newBuffer = new SpokenCon(buffer)
await newBuffer.save()
}
catch (err) {
console.log(err.message);
}
})
//----------Retrieve
spokenBio.get('/get-spoken-bio/:id', async (req, res) => {
const id = req.params.id
try {
const field = await SpokenCon.findById(id)
const binary = field.voice
res.set({ 'Content-Type': 'audio/webm' })
res.send(binary)
}
catch (err) {
console.log(err.message);
}
})

upload an image to amazon s3 in react-native

I am trying to upload image to amazon s3,If possible can any one provide links /docs for how to upload to amazon s3, any help much appreciated
S3 options:
// this.state.s3options in YourComponent
{
"url": "https://yourapp.s3.eu-central-1.amazonaws.com",
"fields": {
"key": "cache/22d65141b48c5c44eaf93a0f6b0abc30.jpeg",
"policy": "eyJleHBpcm...1VDE0Mzc1OVoifV19",
"x-amz-credential": "AK...25/eu-central-1/s3/aws4_request",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-date": "20161125T143759Z",
"x-amz-signature": "87863c360...b9b304bfe650"
}
}
Component:
class YourComponent extends Component {
// ...
// fileSource looks like: {uri: "content://media/external/images/media/13", isStatic: true}
async uploadFileToS3(fileSource) {
try {
var formData = new FormData();
// Prepare the formData by the S3 options
Object.keys(this.state.s3options.fields).forEach((key) => {
formData.append(key, this.state.s3options.fields[key]);
});
formData.append('file', {
uri: fileSource.uri,
type: 'image/jpeg',
});
formData.append('Content-Type', 'image/jpeg')
var request = new XMLHttpRequest();
request.onload = function(e) {
if (e.target.status === 204) {
// Result in e.target.responseHeaders.Location
this.setState({avatarSourceRemote: {uri: e.target.responseHeaders.Location}})
}
}.bind(this)
request.open('POST', this.state.s3options.url, true);
request.setRequestHeader('Content-type', 'multipart/form-data');
request.send(formData);
} catch(error) {
console.error(error);
}
}
// Example display the uploaded image
render() {
if (this.state.avatarSourceRemote) {
return (
<Image source={this.state.avatarSourceRemote} style={{width: 100, height: 100}} />
);
} else {
return (
<Text>No Image</Text>
);
}
}
}
This works for me
import fs from 'react-native-fs';
import {decode} from 'base64-arraybuffer';
import AWS from 'aws-sdk';
export const uploadFileToS3 = async (file) => {
const BUCKET_NAME = 'XXXXXXXXXX';
const IAM_USER_KEY = 'XXXXXXXXXX';
const IAM_USER_SECRET = 'XXXXXXXXXXXXXXX';
const s3bucket = new AWS.S3({
accessKeyId: IAM_USER_KEY,
secretAccessKey: IAM_USER_SECRET,
Bucket: BUCKET_NAME,
signatureVersion: 'v4',
});
const contentType = file.type;
const contentDeposition = `inline;filename="${file.name}"`;
const fPath = file.uri;
const base64 = await fs.readFile(fPath, 'base64');
const arrayBuffer = decode(base64);
return new Promise((resolve, reject) => {
s3bucket.createBucket(() => {
const params = {
Bucket: BUCKET_NAME,
Key: file.name,
Body: arrayBuffer,
ContentDisposition: contentDeposition,
ContentType: contentType,
};
s3bucket.upload(params, (error, data) => {
utils.stopLoader();
if (error) {
reject(getApiError(error));
} else {
console.log(JSON.stringify(data));
resolve(data);
}
});
});
});
};
This worked for me after a significant amount of trying over and over again...
I am also using a lambda function to serve me the link to post with.
The lambda function is just using getSignedUrl.
// Lambda Function
const AWS = require('aws-sdk')
AWS.config.update({
accessKeyId: {bucket_access},
secretAccessKey: {bucket_secret},
signatureVersion: 'v4',
region: {bucket_region}
})
const s3 = new AWS.S3()
exports.handler = async (event) => {
const URL = s3.getSignedUrl('putObject', {Bucket: {bucket_name},
// name of file name being placed in S3 Bucket
// event === metaData object
Key: `${event.{key}}/photo00`})
return URL
};
// React Native
const imagePreview = '{image_uri}'
const handleURL = async () => {
// metaData object
const obj = {
key: "meta_data"
}
const response = await fetch{{lambda_func_endpoint}, {
method: 'POST',
body: JSON.stringify(obj)
})
const json = await response.json();
return json
}
const handleUpload = async () => {
const URL = await handleURL()
const imageExt = imagePreview.split('.').pop()
// I have no idea why you are supposed to fetch before fetching...
// makes no sense. But it works. Lots of trying as I said.
let image = await fetch(imagePreview)
// I have no idea why it needs to be a blob in order
// to upload... makes no sense.
image = await image.blob()
await fetch(URL, {
method: 'PUT',
body: image,
headers: {
Accept: `image/${imageExt}`,
'Content-Type': `image/${imageExt}`
}
})
.then((res) => console.log(JSON.parse(JSON.stringify(res)).status))
.catch((err) => console.error(err))
}
Let me know what you guys think!