I have a Grails web app. Currently I'm serving static image files to my users. I saw this post on StackOverflow that discussed a solution about serving images as stream to hide the source of it. My question is, doesn't that have some trades-off such as putting load on the server since it needs to open a socket in order to stream it to the user v.s just serving it as a static file ?
// controller action
def displayGraph = {
def img // byte array
//...
response.setHeader('Content-length', img.length)
response.contentType = 'image/png' // or the appropriate image content type
response.outputStream << img
response.outputStream.flush()
}
You could then access your image in the src of an tag like this:
<img src="${createLink(controller: 'myController', action: 'displayGraph')}"/>
response.outputStream << img
response.outputStream.flush()
This is not streaming if you mean this: http://en.wikipedia.org/wiki/Streaming_media
It's just using Java OutputStream to provide response content - which is done on the server and response is returned to the client. No socket is kept open.
Related
Functional Overview
I am scraping strings of thumbnail image URL data from an insecure site (http), downloading the images, and then uploading these images to a secure website server (https). I am using cheerio and bluebird to scrape list of website URL's using a mapped promise request, my code is shown below. I push the thumbnail URL image string data from the website URL's to an array stored in the "json" array and then write a file with the contained json data to a suppImages.json file.
Current Issue I am trying to address
Their is a variable number of thumbnail images (around 20 each) contained in the website URL's I am scraping. Right now, my code is set up to aggregate all the thumbnail image URL's into one array. What I would like my code to do is parse the specific thumbnail image URL's into separate arrays per website URL. So basically instead of the output looking like one blob of aggregate data from all the website URL's I want their to be several arrays each contained the discrete thumbnail images displayed on the given website URL.
My code
let fs = require('fs')
const requestPromise = require('request-promise');
const Promise = require('bluebird');
const cheerio = require('cheerio');
const suppURL = require('./output.json');
const urls = suppURL.urli;
console.log("Currently reading URLs from buttons of Realty Warp"+urls)
var json = { pictureThumb: []};
scraper = () => Promise.map(urls, requestPromise)
.map((htmlOnePage, index) => {
const $ = cheerio.load(htmlOnePage);
var linksPic = $(".thumb img");
$(linksPic).each(function(i, link){
var sop = $(this).attr('src');
console.log("sop:" + sop)
json.pictureThumb.push(sop);
});
fs.writeFile('suppImages.json', JSON.stringify(json, null, 6), function(err){
console.log('wrote file');
})
return console.log("URL"+index+':Scrape Complete');
})
.then()
.catch((e) => console.log('We encountered an error' + e));
scraper()
I'm building a web app with AngularJS that will allow users to upload their own images. Right now all of my data is text based, so I am storing the text based data in Firebase. As far as I know, Firebase can't store images. What I want to do is store the user generated images somewhere simple (I'm thinking Amazon S3 or even Dropbox) and then reference the images via unique URLs, which I would store as text in Firebase.
My questions:
Does this seem like a valid approach?
Any recommended services for hosting the images?
How to upload an image to the hosting service and get the image's unique URL?
Right now I am allowing users to upload images on the front end with the following code, just not sure what to do with the images once I have them. Would appreciate any help, I'm very new to this!
HTML
<output id="list"></output>
<input type="file" id="files" name="files[]" class="button" multiple />
Upload Pictures</i>
Angular Controller
$scope.getImages = function(){
$("input[type='file']").trigger('click');
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
console.log(f);
// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
You could use a service like Cloudinary to host images uploaded by your users. There are Angular directives that making using the service pretty easy. You will need a small server-side component to encrypt the upload parameters.
Look into Zapier integration with S3. The idea is that you setup a queue collection in firebase where you would create a new instance with the binary of the file data. Then zapier listens to for child_added on this queue collection and does it's magic (that you don't have to worry about) to upload your file to S3 bucket. After everything is finished, the instance in the queue is deleted... No server side needed with that, except there might be some fees...
Here is the link https://zapier.com/zapbook/amazon-s3/
Currently I am creating an application where I display an uploaded file from CollectionFS and display it using the CollectionFS url return. Locally the image loads and is displayed. However once I deploy the application to meteor.com the image url does not work. With the console indicating that the image path cannot be found.
CollectionFS returns this url for my image:
/cfs/contacts/75erhMtuwjn66fQH3_default1.png
Locally when I deploy the app I can see the image using in my .html:
img src="/cfs/contacts/75erhMtuwjn66fQH3_default1.png"
The specific code returned by CollectionFS is in the .html file and is being called client side:
{{cfsFileUrl "defaultHandler" fileId=fileId collection="Collection"}}
Locally I use this address as the source for the uploaded image in my html:
localhost:port/cfs/contacts/75erhMtuwjn66fQH3_default1.png
But when I attempt to view the image of the deployed app using the same procedure, it returns with an error of url not found:
http://host.meteor/cfs/contacts/75erhMtuwjn66fQH3_default1.png.com
A file is added on a click event client side .js:
'change .fileUploader': function (e) {
var files = e.target.files;
var fileName = files[0].name;
for (var i = 0, f; f = files[i]; i++) {
var k = ContactsFS.storeFile(f);
Session.set('fileID', k);
}}
Server side:
ContactsFS.fileHandlers({
default1: function(options) {
return {
blob: options.blob,
fileRecord: options.fileRecord // if no blob then save result in fileHandle (added createdAt)
};
}});
Then I can call the url in html with the given function from collectionFS documentation:
{{cfsFileUrl "default1" fileId=fileId collection="ContactsFS"}}
Once again the problem isn't generating the url. Both locally and once deployed the app displays a url. It is using the image url as the source for the image tag that is giving me the problem.
CollectionFS had a bug. I contacted the creator with the issue and it is now resolved in the collectionFS package. The report and closed issue can be found at:
https://github.com/raix/Meteor-CollectionFS/issues/85
I want to download files from server with AJAX calls.
It can be done easily by using iframe as follow:
var ifrm = document.getElementById("fileframe");
ifrm.src = "DownloadFile?fileid="+fileid;
But I have more complicated issue.
In some cases, instead of application/octet-stream, response with content type of 'text/html' is given back by the back end.
I plan to use XMLHttpRequest calls to the backed for downloading files.
By using XMLHttpRequest how can I make a browser to popup a save file dialog to handle application/octet-stream?
// connect function handles the XMLHttpRequest communication with the backend
var connection = connect('DownloadFile','fileid='+fileid);
connection.onreadystatechange = function(){
if(connection.readyState == 4) {
var contentType = connection.getResponseHeader('Content-Type');
if(contentType == 'application/octet-stream'){
// here I want to make the browser popup a save file dialog
// for the incoming octet stream
// maybe I can direct stream to an iframe, but how??
}else{
alert(connection.responseText);
}
}
if((connection.readyState == 2)||(connection.readyState == 3)){
}
}
I can think of two options.
Send a HEAD request first and decide the action based on content
type.
Issue a GET request knowing what to expect.
Create a Data
URI (RFC 2387) with the data and open it using window.open.
Browser will open a dialog box for content it cannot render.
I have saved an image to a byte[] in a command object and wish to display it in the next stage of the createFlow webflow.
I am trying to steer clear of using the file system and / or database system for storing the image during the webflow.
Typically, to view the image, I would call renderImage from the gsp:
class ArtefactController {
def createFlow = {
............
}
def renderImage = {
def ArtefactInstance = Artefact.findById(params.id)
if(ArtefactInstance?.image) {
response.setContentLength(ArtefactInstance.image.length)
response.outputStream.write(ArtefactInstance.image)
}
else {
response.sendError(404)
}
}
however for the webflow when I try to call renderFlowImage from a gsp:
def renderFlowImage = {
if(flow.artefactCommand?.image) {
response.setContentLength(flow.artefactCommand.image.length)
response.outputStream.write(flow.artefactCommand.image)
}
else {
response.sendError(404)
}
}
The flow scope in not accessable.
Any suggestions?
I assume you're hitting the renderFlowImage action in your flow's view with a second http request via an img tag such as:
<img src="${createLink(action:'renderFlowImage')}" />
This won't work because, for one thing, you need to pass in a 'flowExecutionKey' which grails uses to match a request with a flow.
It is possible to work around this, but you might be better off just rendering the image during the rendering phase of your flow's next view with a data URI in the img tag. You could do this easily with the rendering plugin, which provides some handy tags (among other things) to render inline images using a data uri.
So in the next flow view, you could replace the existing img tag with a call to one of the rendering plugin's 'inline' tags:
<rendering:inlineJpeg bytes="${artefactCommand.image}" />
But be careful - there are some limitations to the data URI approach to be aware of (see http://www.7cynics.com/webdesign/css/css-inline-images-data-uri.html).