Having spent three days researching and failing to enable user-generated svg upload to the server, I'm completely stumped.
I've built an HTML and Javascript site for users to create a basic model of a vehicle, by entering dimensions into a form.
Those dimensions are then used to edit each svgs 'x' and 'y' coordinates.
I've had success with converting the nested svg to base64 and then downloading to the users filesystem using a button (although it isn't working on JSFiddle), and also experimented with saving the file to local storage.
The output javascript has proven to work absolutely fine, but I just can't figure out how to get that output onto the server in any way.
I'd like to be able to have a user submit their edited svg to the server, where I can then reference it on another page.
I'm fairly unfamiliar with server side code and practise but so far have tried the common suggestions such as:
$.ajax({
url: ajaxurl,
type: 'POST',...
as well as various iterations of PHP code to receive the svg.
Here's the (mostly) working Fiddle of my site so far.
JSFiddle
Edit for better context
This is a very simplified version of the nested svg that I'm trying to export to server
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" id="Layer_1">
<rect id="rect1"/>
<rect id="rect2"/>
</svg>
Export is attached to this button
<button id="downloadSVG">Download</button>
And then the export script
<script>
function downloadSVGAsText() {
const svg = document.querySelector('svg');
const base64doc = btoa(unescape(encodeURIComponent(svg.outerHTML)));
const a = document.createElement('a');
const e = new MouseEvent('click');
a.download = 'download.svg';
a.href = 'data:image/svg+xml;base64,' + base64doc;
a.dispatchEvent(e);
}
const downloadSVG = document.querySelector('#downloadSVG');
downloadSVG.addEventListener('click', downloadSVGAsText);
</script>
Simplified JSFiddle
What I'm completely lost about is how to post the nested svg to the server, so that users can edit or use it later.
So far I understand that I will need Ajax to pass the Base64 image from javascript to PHP file on the server, then the PHP file will place the image on to the server. I just don't seem to be able to make it work. The main issue is how to get the generated Base64 to PHP. The rest I'm happy to work out.
This is a very general question. I think using AJAX is a good way to go. You should be able to just put the SVG in a POST body:
$.ajax('url',{
'data': svg.outerHTML,
'type': 'POST',
'processData': false,
'contentType': 'image/svg+xml'
});
Then use $svgString = file_get_contents('php://input'); to get the contents of the body, this saves you a lot of encoding and decoding. The rest is up to you.
Related
I want to take a snapshot of the webpage from the url. The url is an html web page which is dynamic. Basically we needed an img of that webpage.
I thought to convert the html page to image in c# but din't work.
I first read the html using streamreader and using NReco.ImageGenerator tried to convert into bytes and finally image. This isnt working.
Finally I am trying to convert html to canvas using javascript from inside the html web page.
function report() {
let region = document.querySelector("body");
html2canvas(
$('body'),
{allowTaint: true, logging: true,'onrendered': function (canvas)
{}}).then( //getting problem here at then
function (canvas) {
let jpgUrl = canvas.toDataURL();
console.log(jpgUrl);
var text = "bottom-right Brochure1";
var imageName = text + '.jpg';
download(jpgUrl,imageName, "image/png");
}
Code explained - It will take a snpashot of the body element in the html page using js. Take the url and create the canvas from the url. and automatically download. But i face a issue ------- " html2canvas(...),then is not a function" .. I dont know why its happening. Please help.
Because of Cross-Origin Resource Sharing (CORS) restrictions in all modern browsers, this can't be done purely on the clientside. You need something on the server-side to accomplish this. To do it in javascript on the server use NodeJS there are several npm packages that can help like: node-server-screenshot, PhantomJS etc
In my Vue app I receive a PDF as a blob, and want to display it using the browser's PDF viewer.
I convert it to a file, and generate an object url:
const blobFile = new File([blob], `my-file-name.pdf`, { type: 'application/pdf' })
this.invoiceUrl = window.URL.createObjectURL(blobFile)
Then I display it by setting that URL as the data attribute of an object element.
<object
:data="invoiceUrl"
type="application/pdf"
width="100%"
style="height: 100vh;">
</object>
The browser then displays the PDF using the PDF viewer. However, in Chrome, the file name that I provide (here, my-file-name.pdf) is not used: I see a hash in the title bar of the PDF viewer, and when I download the file using either 'right click -> Save as...' or the viewer's controls, it saves the file with the blob's hash (cda675a6-10af-42f3-aa68-8795aa8c377d or similar).
The viewer and file name work as I'd hoped in Firefox; it's only Chrome in which the file name is not used.
Is there any way, using native Javascript (including ES6, but no 3rd party dependencies other than Vue), to set the filename for a blob / object element in Chrome?
[edit] If it helps, the response has the following relevant headers:
Content-Type: application/pdf; charset=utf-8
Transfer-Encoding: chunked
Content-Disposition: attachment; filename*=utf-8''Invoice%2016246.pdf;
Content-Description: File Transfer
Content-Encoding: gzip
Chrome's extension seems to rely on the resource name set in the URI, i.e the file.ext in protocol://domain/path/file.ext.
So if your original URI contains that filename, the easiest might be to simply make your <object>'s data to the URI you fetched the pdf from directly, instead of going the Blob's way.
Now, there are cases it can't be done, and for these, there is a convoluted way, which might not work in future versions of Chrome, and probably not in other browsers, requiring to set up a Service Worker.
As we first said, Chrome parses the URI in search of a filename, so what we have to do, is to have an URI, with this filename, pointing to our blob:// URI.
To do so, we can use the Cache API, store our File as Request in there using our URL, and then retrieve that File from the Cache in the ServiceWorker.
Or in code,
From the main page
// register our ServiceWorker
navigator.serviceWorker.register('/sw.js')
.then(...
...
async function displayRenamedPDF(file, filename) {
// we use an hard-coded fake path
// to not interfere with legit requests
const reg_path = "/name-forcer/";
const url = reg_path + filename;
// store our File in the Cache
const store = await caches.open( "name-forcer" );
await store.put( url, new Response( file ) );
const frame = document.createElement( "iframe" );
frame.width = 400
frame.height = 500;
document.body.append( frame );
// makes the request to the File we just cached
frame.src = url;
// not needed anymore
frame.onload = (evt) => store.delete( url );
}
In the ServiceWorker sw.js
self.addEventListener('fetch', (event) => {
event.respondWith( (async () => {
const store = await caches.open("name-forcer");
const req = event.request;
const cached = await store.match( req );
return cached || fetch( req );
})() );
});
Live example (source)
Edit: This actually doesn't work in Chrome...
While it does set correctly the filename in the dialog, they seem to be unable to retrieve the file when saving it to the disk...
They don't seem to perform a Network request (and thus our SW isn't catching anything), and I don't really know where to look now.
Still this may be a good ground for future work on this.
And an other solution, I didn't took the time to check by myself, would be to run your own pdf viewer.
Mozilla has made its js based plugin pdf.js available, so from there we should be able to set the filename (even though once again I didn't dug there yet).
And as final note, Firefox is able to use the name property of a File Object a blobURI points to.
So even though it's not what OP asked for, in FF all it requires is
const file = new File([blob], filename);
const url = URL.createObjectURL(file);
object.data = url;
In Chrome, the filename is derived from the URL, so as long as you are using a blob URL, the short answer is "No, you cannot set the filename of a PDF object displayed in Chrome." You have no control over the UUID assigned to the blob URL and no way to override that as the name of the page using the object element. It is possible that inside the PDF a title is specified, and that will appear in the PDF viewer as the document name, but you still get the hash name when downloading.
This appears to be a security precaution, but I cannot say for sure.
Of course, if you have control over the URL, you can easily set the PDF filename by changing the URL.
I believe Kaiido's answer expresses, briefly, the best solution here:
"if your original URI contains that filename, the easiest might be to simply make your object's data to the URI you fetched the pdf from directly"
Especially for those coming from this similar question, it would have helped me to have more description of a specific implementation (working for pdfs) that allows the best user experience, especially when serving files that are generated on the fly.
The trick here is using a two-step process that perfectly mimics a normal link or button click. The client must (step 1) request the file be generated and stored server-side long enough for the client to (step 2) request the file itself. This requires you have some mechanism supporting unique identification of the file on disk or in a cache.
Without this process, the user will just see a blank tab while file-generation is in-progress and if it fails, then they'll just get the browser's ERR_TIMED_OUT page. Even if it succeeds, they'll have a hash in the title bar of the PDF viewer tab, and the save dialog will have the same hash as the suggested filename.
Here's the play-by-play to do better:
You can use an anchor tag or a button for the "download" or "view in browser" elements
Step 1 of 2 on the client: that element's click event can make a request for the file to be generated only (not transmitted).
Step 1 of 2 on the server: generate the file and hold on to it. Return only the filename to the client.
Step 2 of 2 on the client:
If viewing the file in the browser, use the filename returned from the generate request to then invoke window.open('view_file/<filename>?fileId=1'). That is the only way to indirectly control the name of the file as shown in the tab title and in any subsequent save dialog.
If downloading, just invoke window.open('download_file?fileId=1').
Step 2 of 2 on the server:
view_file(filename, fileId) handler just needs to serve the file using the fileId and ignore the filename parameter. In .NET, you can use a FileContentResult like File(bytes, contentType);
download_file(fileId) must set the filename via the Content-Disposition header as shown here. In .NET, that's return File(bytes, contentType, desiredFilename);
client-side download example:
download_link_clicked() {
// show spinner
ajaxGet(generate_file_url,
{},
(response) => {
// success!
// the server-side is responsible for setting the name
// of the file when it is being downloaded
window.open('download_file?fileId=1', "_blank");
// hide spinner
},
() => { // failure
// hide spinner
// proglem, notify pattern
},
null
);
client-side view example:
view_link_clicked() {
// show spinner
ajaxGet(generate_file_url,
{},
(response) => {
// success!
let filename = response.filename;
// simplest, reliable method I know of for controlling
// the filename of the PDF when viewed in the browser
window.open('view_file/'+filename+'?fileId=1')
// hide spinner
},
() => { // failure
// hide spinner
// proglem, notify pattern
},
null
);
I'm using the library pdf-lib, you can click here to learn more about the library.
I solved part of this problem by using api Document.setTitle("Some title text you want"),
Browser displayed my title correctly, but when click the download button, file name is still previous UUID. Perhaps there is other api in the library that allows you to modify download file name.
I have an object containing an array of slides. Every slide can have a media parameter containing the url to an image or video file or the link to a youtube or vimeo video.
My goal is to have the slides viewer zipped and inside the zip I also must have the image or video files taken from the urls.
To create the zip I'm currently using Archiver and it works fine, but I don't know how to put the media files inside the zip (possibly) without writing them on the filesystem first. I think I have to use streams, since archiver.append() can take a stream as first parameter, but I don't know how to do that.
I have implemented some code to understand if the url points to a file or not, writing the files' url inside an array (avoiding youtube or viemo urls).
This is how the zip is created:
...
var urls_array = ["http://url1/file1.jpg", "http://url2/file2.png"]; //the array of urls I take the media files from
var zip = archiver('zip');
zip.pipe(res);
zip.directory(__dirname + '/../../zip/', 'slideshow');
zip.append( new Buffer( file ), { name: 'slideshow/assets/slides.json' });
zip.finalize();
I suppose I have to cycle the url_array and for each url perform a http.get() call, but I can't understand how to .pipe() the response inside the zip.
Is there anyone who can help me?
Don't hesitate to ask me more information :)
Thank you in advance.
You should use the request method to create a stream from a remote URL to be passed to the append function of archiver, as a first argument, like this:
for ( var slide in slides ) {
archive.append( request( slide.url ), { name: slide.name } );
}
see archiver.append documentation ( https://archiverjs.com/docs/module-plugins_zip-Zip.html#append )
Hope it helps.
When one right clicks an image in a webpage and selects "Properties", one gets a display which details the URL, Size, Dimensions, Created (date), Modified (date), etc.
Obviously I know the URL as it is in my code, but some of the others are not, for example created and modified dates, they have to be coming from the server at the image source. I have tried using the
document.getElementById("photo").attributes
where the related HTML code is this
<img id="photo" src="http://www.somesite.com/photo.jpg" width="320" height="240" alt="offline" >
but that only returns the 5 attributes found in the above img line.
Since the dates and other information are available for the "Properties Dialogue" I assume there are also available somewhere in the document object.
Any ideas as to how I might access them using JavaScript or VBScript?
Thanks....RDK
Have you investigated using the JavaScript Image object?
Well, you could try get the the URL of the image, then do an AJAX request to the image, get the response headers and they will tell you the created date, etc.. that you are after...
Example...
var myImageURL = %YOUR_IMAGE_URL%
var modifiedHeader = 'Last-Modified';
var req = new XMLHttpRequest();
req.open('HEAD', myImageURL, false);
req.send();
var header = req.getResponseHeader(modifiedHeader);
if (header) {
console.log(header); // this is the last modified date
}
EDIT:
This would work only if the image was accessible via AJAX... (same-origin policy, etc...)
EDIT:
Changed to head so SOP does not matter now.. Thanks #Musa
Information like the creation and modification dates will be stored (if anywhere) in the image's EXIF metadata.
There is a jQuery library for reading EXIF metadata that provides an exif() method, e.g. $('img').exif('DateTimeOriginal').
Note that images hosted on a different domain will not be accessible to JavaScript.
Information about the width and height of the original image file should be properties of the image object itself, e.g. document.getElementById('photo').naturalHeight
I have some text data (say var a = 'Hello World From Javascript';)in javascript variable in current window. I want to do the following
through javascript-
1. open a new window and write the text data to the window.
2. set the content type to text/plain.
3. set the content-disposition to attachment, so that download prompt comes.
4. user downloads the text data as a text file and saves it to his local disk.
is this all possible through javascript?
I know we can make ajax calls to server or redirect but in this case instead of following above steps. But in this case, these workarounds are not adaptable.
you can do that using JS & HTML5 features. Please find below a sample code.
var fileParts = ['Hello World From Javascript'];
// Create a blob object.
var bb = new Blob(fileParts,{type : 'text/plain'});
// Create a blob url for this.
var dnlnk = window.URL.createObjectURL(bb);
var currentLnk = $('#blobFl').attr('href');
// blobFl is the id of the anchor tag through which the download will be triggered.
$('#blobFl').attr('href',dnlnk);
$('#blobFl').attr('download','helloworld.txt');
// For some reason trigger from jquery dint work for me.
document.getElementById('blobFl').click();
Triggering a file download without any server request
Unfortunately this is not something you can do with normal browser capabilities. Something like flash or a browser-specific plugin will get you what you need, but security limitations within javascript will not let you download arbitrary data created within the browser.
Also the 'data' url is not supported across all browser/version combinations. I am not sure if your users are constrained on what browser they are using or not but that may limit what you can do with that solution.
Source: Triggering a file download without any server request
If you already have the file on the server (I make an ajax call to generate and save a PDF on the server) - you can do this
window.location.replace(fileUrl);
No, Content-Disposition is a response header, it has to come from the server. I think you could do it with Flash but I wouldn't recommend it.
Here's a clean, pure js version of #Rajagopalan Srinivasan's answer:
var fileParts = ["Hello World From Javascript"];
// The anchor tag to use.
const blobLink = document.getElementById("blobLink");
// Create a blob object.
var blob = new Blob(fileParts, { type: "text/plain" });
// Create a blob url for this.
var blobUrl = window.URL.createObjectURL(blob);
blobLink.setAttribute("href", blobUrl);
blobLink.setAttribute("download", "helloworld.txt");
blobLink.click();
<a id="blobLink">Download</a>