File copying using NodeJS - javascript

I am trying to copy a file from one location to another location. Here is my code below and I am calling this script like [1] http://localhost:8000/prdcopy/acbd.pdf
var http = require('http');
var fs = require('fs');
var express=require('express');
var app=express();
var path_upload = "/234.567.890.123/";
var path_prodn = "//123.345.678.999/sample/temp/";
app.get('/prdcopy/:file',function(req,res){
var rstream = fs.createReadStream(path_upload + req.params.file);
var wstream = fs.createWriteStream(path_prodn + req.params.file);
rstream.pipe(wstream);
res.end();
rstream.on('end', function () {
console.log('SrcFile');
});
wstream.on('close', function () {
console.log('Done!');
});
});
var server=app.listen(8000,function(){
console.log("listening on port 8000...");
});
It copies the file properly however after copying the Firefox browser opens up a PDF reader. There is no file loaded in it though. This is my first node script and I would like to know what is that I am doing wrong. In IE it is not opening any PDF Reader window.

This is not necessarily an error.
With res.end() you are sending back an http response with no Content-Type header. What Firefox does in this case is detecting the .pdf at the end of the typed URL and assuming that the response will contain something that is displayable by the PDF viewer. This is not the case as you are not sending anything back (no body).
Try substituting res.end() with something like:
res.header("Content-Type", "text/html");
res.end();
You will see that no PDF viewever is displayed even in Firefox. You could also use other res methods that automatically set the content type for you. For instance, to send a json response back substitute res.end() with:
status = {};
status.message = "Copied successfully";
res.json(status);

Related

How to download a json object as file?

I am currently trying to let the user of my website download a json object as a json-file.
With my following code i get the error message:
Error: Can't set headers after they are sent.
at SendStream.headersAlreadySent (\node_modules\send\index.js:390:13)
at SendStream.send (\node_modules\send\index.js:617:10)
at onstat (\node_modules\send\index.js:729:10)
at FSReqCallback.oncomplete (fs.js:168:5)
router.post('/about', ensureAuthenticated,
function (req, res, next) {
console.log(req.user);
var jsonVariable = JSON.stringify(req.user);
var path_tmp = create_tmp_file(jsonVariable);
res.download(path_tmp);
res.redirect('/about');
next();
}
);
Is there a better way to download a json object directly with no need to save it in the filesystem?
You can always inject some HTML into a page (or redirect to a page with some client-side JavaScript on it), and download it with the client. Just send the JSON string somehow to the new page you are redirecting to (it can even be a GET parameter), then download it with the following code (assuming the JSON string is in a variable called json):
var a = document.createElement("a")
a.href = URL.createObjectURL(
new Blob([json], {type:"application/json"})
)
a.download = "myFile.json"
a.click()
var jsonVariable = JSON.stringify(req.user);
var path_tmp = create_tmp_file(jsonVariable);
res.download(path_tmp);
Send the data with res.json and use content-disposition to make it a download.
res.set('Content-Disposition', 'attachment; filename=example.json')
res.json(req.user);
res.redirect('/about');
next();
And don't do that (which is the cause of the error). You are responding with a download. You can't say "Here is the file you asked for" while, at the same time say, "The file you asked for isn't here, go to this URL instead".

Express is not rendering the page in the browser if called from a client-side JS function via XHR

In order to pre-process some data from the browser, i'm triggering Express from the browser via a JS XHR call. The problem is that the browser is apparently not showing the page rendered by Node/Express on the server
Not a matter of file paths, views etc. Other EJS templates get rendered perfectly when invoked by clicking on a object in the browser
HTML code:
<button type='button' class='button' onclick='sendData()'>Action</button>
<script language='javascript'>
function sendData() {
var http = new XMLHttpRequest();
const filename = document.getElementById('fileselect').files[0];
// reads the file and pass it to the server as a string
var reader = new FileReader();
reader.readAsText(filename);
reader.onload = function() {
var fileContent = reader.result;
var obj = 'file=' + fileContent;
obj = encodeURI(obj);
http.addEventListener('load', function (event) {
//alert('Yeah! Data sent and response loaded.');
});
// Define what happens in case of error
http.addEventListener('error', function (event) {
alert('Oops! Something went wrong.');
});
http.open('POST', '/action', true);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.send(obj);
</script>
NODE:
app.post('/action', function(req, res){
//console.log(req.body);
const file = req.body.file;
// process the data
...
res.render("page.ejs");
the page.ejs does NOT get rendered in the browser in response
You've done nothing to render it there.
The point of Ajax is that you are making an HTTP request from and handling the response with JavaScript.
You are not navigating the browser to a new page.
If you want to do something with the response (like display it in the page) then you need to do that here:
http.addEventListener('load', function (event) {
//alert('Yeah! Data sent and response loaded.');
});
The data will be in this.responseText.
However, if you want that … then you should probably be using a regular form submission and not using Ajax at all.

NodeJS sending an image(which may be modified) to the client with middleware

I want to mention that the image file is changing continuously.
I'm using the middleware of NodeJS:
app.use("/image.jpg",express.static(_dirname+"/image.jpg")
The problem is that the Node conveys the image.jpg without really telling that the file has been modified.
Pressing a button this part will occur.
var image=new Image();
image.onload=function(){rendering to canvas}
image.src="/image.jpg";
Somehow there is a problem...
The server's picture file gets modified then it emits to the client to draw the image, regarding to the results, the client is rendering the first image it has loaded again, although the image on the url has been changed.
I think the client is caching the image? and thinks that the image is unmodified so it keeps using it.
Is there a way to draw the current image on the url?
Are there even better methods?
You can use the fetch() API to implement cache-busting without cluttering your client's browser cache with a bunch of /image.jpg?bust=... resources.
After deciding on a folder in your server that you want to allow static access to changing files (this is preferable to the pattern where you allowed static access to a single file), you can implement your real-time updates using fs.watch() like so:
Node app.js (with express 3/4):
const fs = require('fs')
const path = require('path')
const app = require('express')()
const server = require('http').Server(app)
const io = require('socket.io')(server)
server.listen(process.env.PORT || 8080)
app.use('/', express.static(path.resolve(__dirname, './watched-directory')))
//...
io.on('connection', (socket) => {
//...
});
fs.watch('watched-directory', {
//don't want watch process hanging if server is closed
persistent: false,
//supported on Windows OS / Mac OSX
recursive: true,
}, (eventType, filename) => {
if (eventType === 'change') {
io.emit('filechange', filename)
}
})
Browser index.html:
<script src="/socket.io/socket.io.js"></script>
<script>
let socket = io.connect()
socket.on('filechange', async (filename) => {
console.log(filename)
let response = await fetch(filename, { cache: 'no-store' })
let blob = await response.toBlob()
let url = URL.createObjectURL(blob)
//modified from your question
let image = new Image()
image.addEventListener('load', () => {
//your canvas rendering code here
})
image.src = url
})
</script>

"Echoing" an image in Node.js

I have a fully functioning PHP application that I am trying to make a Node.js version of. It deals with serving image tiles. When it's ready to display the image it does:
// Stream out the image
echo self::$tile;
How would I do something similar in Node.js? I understand this is a broad question, but I think my biggest issue is that I don't understand how PHP "echoes" an image.
Details:
I'm using AWS to get the image. The AWS call returns a Buffer. At this point of time, in the Javascript I have left the image as a Buffer.
The site populates a map with tiled images, so there are multiple calls with the image placed at a particular location on the page. I am using express to handle the requests.
app.get(/^\/omb\/1.0.0\/(.+)\/(.+)\/(.+)\/(.+)\.[a-zA-Z]*$/, function(req, res){
var MosaicStreamer = require('./models/MosaicStreamer.js');
var ms = new MosaicStreamer;
var configs = {library: req.params[0], zoom: req.params[1], column: req.params[2], row: req.params[3]};
ms.handleTile(configs);
});
handleTile grabs the image and ultimately brings me to where I am now. The image is grabbed using the following:
var aws = new AWS.S3();
var params = {
Bucket: this.bucket,
Key: this.tileDirectory + this.filepath,
Range: 'bytes=' + (this.toffset + 4) + "-" + (this.tsize + this.toffset + 4)
};
var ts = this;
aws.getObject(params, function(err, data){
if(ts.tile == null){
ts.tile = data.Body; //S3 get object
}
}
I think what you want to do is take a given URL which represents closely the naming convention of folders/files in your S3 Bucket. So assuming that you've established a client connection to your S3, you can use the readFile method. The 2nd argument is an imageStream which you can pass in the response. Once the stream has ended from S3, it will automatically end the res from the client, outputting the image directly to the client (as you intend).
Some psuedo code:
app.get(/^\/omb\/1.0.0\/(.+)\/(.+)\/(.+)\/(.+)\.[a-zA-Z]*$/, function(req, res){
var MosaicStreamer = require('./models/MosaicStreamer.js');
var ms = new MosaicStreamer;
var configs = {library: req.params[0], zoom: req.params[1], column: req.params[2], row: req.params[3]};
return ms.handleTile(configs, res);
//return the handleTile function, add 2nd argument and pass res through
});
Inside of handleTile function you can make the call for the S3
function handleTile(configs, res){
client.readFile('filename', function(error, imageStream){
imageStream.pipe(res);
});
}
Now requests to images like this:
<img src="/path/to/my/file/that/matches/regexp/expression"/>
It will request that image from the S3 Bucket and stream the resource back to the client directly.
To successfully render an image, you have to implement three steps:
Retrieve the image data (for instance as a Buffer read via fs.readFile) or a stream (for instance via fs.createReadStream
Set the appropriate headers in the web request handler with the arguments (req, res); something like
res.writeHead(200, {'Content-Type': 'image/png'});
Write the file. If you have the file in a Buffer, with
res.end(buf, 'binary');
If you have a stream via
read_stream.pipe(res)
The whole code may look like (assuming you want to serve the file image.jpg from the current directory):
'use strict';
var fs = require('fs');
var http = require('http');
http.createServer(function(req, res) {
fs.readFile('image.jpg', function(err, buf) {
if (err) {
res.writeHead(500);
res.end('Cannot access file.');
return;
}
res.writeHead(200, {'Content-Type': 'image/jpeg'});
res.end(buf, 'binary');
});
}).listen(8002, '');
Using a stream, a very simple version (beware: no error handling, with error handling it can get a little bit more complex, depending how you want to handle errors occurring while the file is being read)
'use strict';
var fs = require('fs');
var http = require('http');
http.createServer(function(req, res) {
var stream = fs.createReadStream('image.jpg');
// Error handling omitted here
res.writeHead(200, {'Content-Type': 'image/jpeg'});
stream.pipe(res);
}).listen(8003, '');
Code that uses a Buffer is easier to write, but means that your server must hold the whole file in memory - for instance, you will be unable to serve a 320 Gigapixel image file. You also only start sending data once you have the whole file.
Using a stream allows sending the file as soon as you get it, so it will be a little faster. If you're reading from file or a local fast server the speed difference is likely negligible. In addition, you'll only need a little bit of memory. On the other hand, error handling is more complex.

Use "localhost" instead of "app://" to display files in TideSDK

Is there any way to load/display files by using 'localhost' address instead of 'app://'?
I mean something like:
http.://localhost/com.app/file.swf
I've tried using HTTPServer & FileStream listed on TideSDK documentation, but every time I try to load a file, the entire app gets blocked and stops responding.
server = Ti.Network.createHTTPServer();
//Specify port number and callback function
//This example can be tested by pointing your
//browser to http://localhost:8082/
server.bind(8082,'localhost',function(request,response) {
//Serve desired file
var contents,
contentType,
readFi = Ti.Filesystem.getFile(Ti.App.getHome()+'/flash',request.getURI());
if (readFi.exists())
{
var Stream = Ti.Filesystem.getFileStream(readFi);
Stream.open(Ti.Filesystem.MODE_READ);
contents =Stream.read();
Stream.close();
}
response.setContentType("application/x-shockwave-flash");
//Setting content length of the response
response.setContentLength(readFi.size());
//Setting status and reason
response.setStatusAndReason('200','OK');
//Finally writing the response back
response.write(contents);
});

Categories

Resources