script to query data from BigQuery, export result to CSV format, and sFTP transfer to destination folder - google-bigquery

I am looking for a reliable script to perform sFTP transfer from Google Cloud Storage bucket to the destination sFTP folder. Currently I'm using pandas/python to query data from Big Query and save the result to Google Storage and it's working flawlessly, and I use Google Cloud function to do the sFTP transfer and it's been working sporadically, sometimes it didn't run, and other times the file transfer was incomplete, and also it takes very long time for it to run (around 15 min via Google could function versus 5 secs if I transfer the same file manually). Attached is the Cloud function currently used. Any help is very much appreciated. Thanks
/**
* Triggered from a change to a Cloud Storage bucket.
*
* #param {!Object} event Event payload.
* #param {!Object} context Metadata for the event.
*/
exports.gcfSendToSftp = (event, context) => {
const Storage = require('#google-cloud/storage');
const os = require("os");
const fs = require("fs");
const Client = require('ssh2-sftp-client');
// Creates a client
const storage = new Storage();
const path = require('path');
const cwd = os.tmpdir()
const MyDate = new Date();
const formatted_year = MyDate.getFullYear();
const formatted_month = ('0' + (MyDate.getMonth()+1)).slice(-2);
const formatted_day = ('0' + MyDate.getDate()).slice(-2);
TRANSACTIONS_LOG_FILENAME='W2G_TransactionsLog_' + formatted_year + formatted_month + formatted_day + '.csv'
destFilename = path.join(cwd, TRANSACTIONS_LOG_FILENAME)
srcFilename=TRANSACTIONS_LOG_FILENAME
async function sendSFTP() {
const stats = fs.statSync(destFilename)
const fileSizeInBytes = stats["size"]
console.log(destFilename + " file size in bytes "+ fileSizeInBytes);
// let data = fs.createReadStream(destFilename);
let data = destFilename;
let remote = '/Home/w2g/W2GInventoryIntegration/'+ srcFilename;
let client = new Client();
client.connect({
host: '111.11.11.11',
port: '22',
username: 'test',
password: 'hokcqqEHH10g',
readyTimeout: 0,
debug: console.log
}).then(() => {
client.fastPut(data, remote);
}).catch(err => {
console.log(err, 'catch error');
});
}
async function downloadFile() {
bucketName='data_export_vw_transactions_log';
const options = {
// The path to which the file should be downloaded, e.g. "./file.txt"
destination: destFilename,
};
// Downloads the file
await storage.bucket(bucketName).file(srcFilename).download(options);
console.log(
`gs://${bucketName}/${srcFilename} downloaded to ${destFilename}.`
);
}
downloadFile().then( () => { sendSFTP() })
};

Related

Track upload progress using Peerjs

Is there any method to track the upload progress while sending file/data to a WebRTC peer using peerjs?
import Peer from 'peerjs';
const peer = new Peer(peerId, { host: 'cloudbreeze-peer.onrender.com', secure: true })
peer.on('connection', conn => {
const file = html_file_object
const peerName = conn.metadata.username || 'Anonymous user'
conn.on('open', () => {
conn.send({ file, name: file.name, size: file.size, type: 'file' })
// track the progress of the file sent ('progress' listener is not available)
conn.on('progress', bytes => console.log(bytes * 100 / file.size))
})
}
I tried a way to divide the file into small chunks, it helped to track the progress on the receiving end, but I still couldn't track the uploading progress.
import Peer from 'peerjs';
const peer = new Peer(peerId, { host: 'cloudbreeze-peer.onrender.com', secure: true })
peer.on('connection', conn => {
const file = html_file_object
const peerName = conn.metadata.username || 'Anonymous user'
conn.on('open', () => {
const chunkSize = 1024 * 1024 // In bytes
const chunks = Math.ceil(size / chunkSize)
for (let i = 0; i < chunks; i++) {
const offset = i * chunkSize
conn.send({ file: file.slice(offset, offset + chunkSize), name, size, type: 'file' })
// still no way to track the progress
}
})
}
Thanks in advance for helping me out!
sent back received data percentages info to that sender as a text message

How to upload image from React Native + Expo app to Azure Blob Storage?

When I try to upload Image from React Native + Expo app to Azure Blob Storage via Azure Functions (Node.js), image(jpeg) can be saved into the storage but that image cannot be opened correctly.
Evidence
== ASK ==
・How can I upload image? (Current behavior explained in below troubleshooting steps is unexpected? if so, how can I prevent it and upload image precisely..?)
== Assumption ==
・React Native 0.63.2
・Expo 42.0.4
・Expo-Image-Picker 10.2.3
・Request Route :
React Native + Expo App -> API (Azure Functions (Node.js)) -> Azure Blob Storage
== My Code ==
= React Native + Expo (Client Side)
*I implemented based on below discussion:
How can I upload a photo with Expo?
*params : returned value from expo-image-picker after image select
const formData = new FormData();
const imageUri = params.uri;
const dataType = mime.getType(imageUri);
const fileName = imageUri.split("/").pop();
formData.append('image',{
uri: imageUri,
name: "a.jpg",
type: dataType
} as any);
const url = 'xxxxx';
await fetch (url, {
method: 'POST',
headers:{
},
body:formData
})
expo-image-picker side code
(pass the value of "result" to the above code.)
const pickImage = async () =>{
console.log("PICKIMAGE CALLED.");
// Access Permission Check
let permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (permissionResult.granted === false) {
alert("Permission to access camera roll is required!");
return;
}
// Image Pickup
try{
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: false,
aspect: [4,3],
quality: 0.5,
base64: true,
});
if (!result.cancelled) {
setImageData(result);
}
} catch(E){
console.log(E);
}
};
= Azure Function (Server Side)
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const replace = require('buffer-replace');
// Upload Image
try{
// Parse Blob Data
var multipart = require("parse-multipart");
var bodyBuffer = Buffer.from(req.body);
var boundary = multipart.getBoundary(req.headers['content-type']);
var parts = multipart.Parse(bodyBuffer, boundary);
// Upload Blob to Azure Storage
const {BlobServiceClient} = require("#azure/storage-blob");
const containerName = "ContainerName";
const blobName = parts[0].filename;
const blobServiceClient = BlobServiceClient.fromConnectionString("connection string of storage");
const containerClient = blobServiceClient.getContainerClient(containerName);
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
const uploadBlobResponse = await blockBlobClient.upload(parts[0].data, parts[0].data.length);
context.log("Blob was uploaded successfully. requestId: ", uploadBlobResponse.requestId);
context.done();
== Troubleshootings ==
1, When I checked Azure Function side log, "parts" seems have incorrect binary(hex) data.
I mean, before "ff d8" (indicator of jpeg format), unexpected line break "0d 0a" seems added. I doubt this is the reason file cannot be opened precisely.
var parts = multipart.Parse(bodyBuffer, boundary);
Result of context.log(parts)
2, Based on the result of 1, I also checked same thing from client side (React Native + Expo) with below code, and found unexpected line break "0d 0a" is not there. So wondering why unexpected line break is added while processing at server side.
function base64ToHex(str) {
const raw = atob(str);
let result = '';
for (let i = 0; i < raw.length; i++) {
const hex = raw.charCodeAt(i).toString(16);
result += (hex.length === 2 ? hex : '0' + hex);
}
return result.toUpperCase();
}
console.log(base64ToHex(params.base64));
Result of Client Side
3: Try Image Upload from Postman.
Request Route : Postman -> API (Azure Functions) -> Azure Blob Storage.
The way to send image is little different from that of React Native + Expo app (Postman: seems add image file (blob?) directly into the formdata, ReactNative: add blob uri, mime etc into formdata), but It succeeded.
Thank you very much for your help!!

How to create a file in Amazon S3 Node Lambda function

I am trying to create and load an object into an s3 container but the PUT event does not seem to create the object
const AWS = require('aws-sdk');
const fs = require('fs');
const s3 = new AWS.S3();
exports.handler = async (event) => {
// I've tried the full arn as well as just the bucket name
const bucket ='someBucketName';
const key = 'sample.csv';
// for testing purposes I am just loading a 30 row csv file.
// I've also tried a json object with no luck
// ex: const data = JSON.stringify({'label': 'foo'});
const data = fs.readFileSync('trees.csv','utf-8');
const params ={
Bucket : bucket,
Key : key,
Body: data
};
await s3.putObject(params, function (err, data) {
if(err){
console.log(`Error creating file ${err.stack}`);
}else{
console.log('File Created');
}
});
};
When I execute the lambda it runs with no errors. But no file is ever created in the bucket and I never see the output of 'File Created'. If I add a console.log() prior to the put I can see the params are all set. The lambda has full s3 access as well as full access to cloudWatch. This is just a test so I am running it directly from the lambda console in aws.
Any suggestions on what I am missing or doing wrong?
Found the answer for those interested. I omitted the promise() on the putObject function.
const AWS = require('aws-sdk');
const fs = require('fs');
const s3 = new AWS.S3();
exports.handler = async (event) => {
const bucket ='someBucketName';
const key = 'sample.csv';
const data = fs.readFileSync('trees.csv','utf-8');
const params ={
Bucket : bucket,
Key : key,
Body: data
};
await s3.putObject(params, function (err, data) {
if(err){
console.log(`Error creating file ${err.stack}`);
}else{
console.log('File Created');
}
}).promise();
};

Upload Image into S3 bucket using Api Gateway, Lambda funnction

I'm trying to upload the image (base64) from the postman, I can see when I hit the Serverless API, something has been added in S3 bucket but not image, I'm using nodejs Lambda function, I tried so many solutions but that didn't work out. Please suggest me where I'm wrong:
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const moment = require('moment');
const fileType = require('file-type');
const sha1 = require('sha1');
const multipart = require('parse-multipart');
exports.handler = function (event, context, callback) {
let request = event.body;
// get the request
let base64String = request.base64String;
// pass the base64 string into a buffer
let buffer = new Buffer(base64String, 'base64');
let fileMime = fileType(buffer);
// check if the base64 encoded string is a file
if (fileMime === null) {
return context.fail('The string supplied is not a file type');
}
let file = getFile(fileMime, buffer);
// let file = getFile(fileMime, parts);
let params = file.params;
s3.upload(params, function (err, data) {
// putObject(params, function (err, data) {
if (err) {
console.log(err);
callback(err);
}
// if the file object is uploaded successfully to
// s3 then you can get your full url
console.log('File URL', file.full_path + JSON.stringify(data));
callback(null, data);
});
}
let getFile = function (fileMime, buffer) {
// get the file extension
let fileExt = fileMime.ext;
let hash = sha1(new Buffer(new Date().toString()));
let now = moment().format('YYYY-MM-DD');
let filePath = hash + '/';
let fileName = now + '.' + fileExt;
let fileFullName = filePath + fileName;
let fileFullPath = 'https://console.aws.amazon.com/s3/buckets/bucket-name/images/' + fileFullName;
console.log('fileFullPath' + fileFullPath);
let params = {
Bucket: 'bucket-name',
Key: fileFullPath,
// 'this is simply the filename and the extension, e.g fileFullName + fileExt',
Body: buffer
};
let uploadFile = {
size: buffer.toString('ascii').length,
type: fileMime.mime,
name: fileName,
full_path: fileFullPath
}
return {
'params': params,
'uploadFile': uploadFile
}
}
Please let me know, where I'm missing some piece of codes. That will be appreciated.
When uploading objects to S3 that should be opened in a web browser, you need to set the correct content type. When S3 serves your object, the content type you set will be sent as a HTTP header.
Web browsers use MIME types to determine how to process URLs, not file extensions.
In your case, the content type is missing from the parameters your are passing to s3.upload. Moreover, the key is incorrect.
It should be:
const params = {
Bucket: 'bucket-name',
Key: fileFullName // must be a path within your bucket and not an url like you have in fileFullPath,
Body: buffer,
ContentType: fileMime.mime // Sets the content type header, without this your browser cannot infer the type (browsers use mime types, not file extensions)
};

Expo: Anyway of adding Speech to Text?

I want to include speech-to-text in my Expo app.
There are api's available such as google's speech to text and watson etc...
Has anyone come up with a solution or has any advice on how to include Speech-to-Text in their Expo or React-Native application?
I have looked at various github repos that provide Speech-to-Text for React-Native applications but they do not look production ready, and are strictly React-Native solutions as you need access to Java/Swift code.
I am not disinclined towards that if that is the only option but would prefer a Expo solution if possible.
Regards,
Emir
If you want to implement Speech-To-Text on expo then you need to create an api and deploy it else you need to detach the project and use the library react-native-google-speech-api
This is what i have implemented using google app engine, google cloud storage and google speech to text.
const format = require('util').format;
const fs = require('fs');
const express = require('express');
const multer = require('multer');
const requestHttp = require('request');
const {Storage} = require('#google-cloud/storage');
// Instantiate a storage client
const storage = new Storage();
// const upload = multer();
const app = express();
// Imports the Google Cloud client library
const speech = require('#google-cloud/speech');
// Creates a client
const client = new speech.SpeechClient();
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
const encoding = 'LINEAR16';
const sampleRateHertz = 16000;
const languageCode = 'en-US';
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 5 * 1024 * 1024, // no larger than 5mb, you can change as needed.
},
});
const bucket = storage.bucket(process.env.GCLOUD_STORAGE_BUCKET);
app.post('/upload', upload.single('file') , async (req, res) => {
const file = await req.file
if (!file) {
const error = new Error('Please upload a file')
error.httpStatusCode = 400
return next(error)
}
// Create a new blob in the bucket and upload the file data.
const blob = bucket.file(req.file.originalname);
const blobStream = blob.createWriteStream({
resumable: false,
});
blobStream.on('error', err => {
next(err);
});
blobStream.on('finish', async () => {
// The public URL can be used to directly access the file via HTTP.
const publicUrl = await format(
`https://storage.googleapis.com/${bucket.name}/${blob.name}`
);
const request = {
config: {
encoding: encoding,
sampleRateHertz: sampleRateHertz,
languageCode: languageCode,
},
audio: {
uri: 'gs://YOUR-Bucket-Name/File-name.ext'
}
};
// Stream the audio to the Google Cloud Speech API
const [response] = await client.recognize(request);
const transcription = response.results
.map(result => result.alternatives[0].transcript)
.join('\n');
console.log(`Transcription: `, transcription);
res.status(200)
.send({
success: 'true',
message: 'Text retrieved successfully',
text: transcription
})
.end();
});
blobStream.end(req.file.buffer);
});
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});