4

Hi I'll introducing my problem directly with the use case: I'm working on a sort of configurator and I have a web page (let's call it page1) where the user can upload an image. The image is showed normally in an img tag. When the user has uploaded the image he can go forward on the next page (let's call it page2). Now, on this page2 I want to show the image uploaded by user without storing the image. This is what I've tried 'till now:

Before to do this I've inserted in the json the src attribute of the image directly. But the browser gave me te error that the GET request is too long or something like that. So, I've found as solution to convert the image with base64:

function getBase64Image(img) {
  var canvas = document.createElement("canvas");
  canvas.width = img.width;
  canvas.height = img.height;
  var ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  var dataURL = canvas.toDataURL("image/png");
  return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
function sendSelectedProducts()
{
   var logo = document.getElementsByName('logo-container')[0];

   var base64 = getBase64Image(logo);
   console.log(base64);
   var json_string = '{"img": "' + base64 + '" }';
   console.log(json_string);

   window.location.replace('riepilogo.php?prodotti=' + json_string);

}

So, what I'm doing with this code is to send a Json with a base64-converted image. On the PHP-side this is the code that tries to convert back the image:

function base64ToImage($base64_string, $output_file) {
   $file = fopen($output_file, "wb");
   $data = explode(',', $base64_string);
   fwrite($file, base64_decode($base64_decode));
   fclose($file);
   return $output_file;
}
echo '<p>'.$img.'</p>';
echo '<a class="cart-image" href="#"><img src="'.base64ToImage($img, 'logo_file.jpg').'" alt="logo"></a>'?>

This code doesn't work and I'm looking for solution that does not store the image on the server. How can I do?

chaw359
  • 505
  • 1
  • 5
  • 14
  • What do you mean by "not storing the image" -- where would the image *not* be stored? Where do you expect a Web page to load the image from? – Armen Michaeli Jun 19 '19 at 13:43
  • Firstly, if you're going to upload a file it would make a lot more sense to do it via a form upload (so generating a POST request), rather than a GET. Then you don't have any restrictions on the size, and you don't need to mess about with JSON or base64. You can use JavaScript to fake a form upload if need be. – ADyson Jun 19 '19 at 14:17
  • If you literally only want to re-use this data on the very next page (which seems a bit pointless to me, but ok maybe there is a use case), then you could just get the data from the uploaded file (which, when uploaded by a form, is stored in the tmp directory by default, and discarded by PHP at the end of the request), base64 encode it and place the base64 data into an img tag. No need to write it to disk. Alternatively you could get rid of the server-side interaction entirely and use localstorage in the browser, then it never needs to touch the server at all. That might be more efficient... – ADyson Jun 19 '19 at 14:17
  • ... but it depends if you need to validate, server-side (i.e. securely), that this image has been provided and is acceptable before moving forward? If you do, then surely a persistent copy would be needed as future evidence of that? I don't know what the scenario is, but this is, IMO, a slightly unusual requirement. Why does the user need to provide an image which you aren't going to keep? – ADyson Jun 19 '19 at 14:19
  • @ADyson I don't want to store the image because I'm developing a configurator in which a user can customize the object uploading an image, but the user could not complete the process. – chaw359 Jun 20 '19 at 09:33
  • " I'm developing a configurator in which a user can customize the object uploading an image" ...I literally have no idea what any of that means, sorry. What's a "configurator" when it's at home? Try and describe your goal in general terms without using jargon specific to your project. – ADyson Jun 20 '19 at 10:29
  • @ADyson I encourage you to vote to close this -- the question seems either abandoned or ignored. There is probably a simple problem waiting to be solved, but without details I, for one, don't know what else to do. – Armen Michaeli Jun 24 '19 at 11:30

3 Answers3

1

You can save the selected file (for example in a window var or local storage) and render the image in the next page using canvas.

See following example, please:

function saveImage(img) {
    window.file = img.files[0];
}

function loadImage() {
    let ctx = document.getElementById('canvas').getContext('2d');
    let url = URL.createObjectURL(window.file);
    let img = new Image();
    img.onload = function() {
        ctx.drawImage(img, 20, 20);    
    }
    img.src = url;
}
<input type="file" id="imgFile" onchange="saveImage(this);" />

<button onclick="loadImage()">Load image</button>

<canvas width="400" height="300" id="canvas"/>

You should put saveImage in the first page and loadImage in the second page.

I hope it was clear. Bye.

Alessandro
  • 4,382
  • 8
  • 36
  • 70
1

You don't need to decode the image from base64, just do this:

echo '<a class="cart-image" href="#"><img src="data:'.$img.'" alt="logo"></a>'?>

where $img is the image in base64 format. For more info see: How to display Base64 images in HTML?

gbalduzzi
  • 9,356
  • 28
  • 58
  • I tried but doesn't work. I don't have 'data:image/png;base64' "prefix" in my base64 image. I'had to convert the image in base64 because otherwise the GET didn't work. – chaw359 Jun 20 '19 at 09:37
  • There is no way you can use the image base64 value if you don't know the image format. If you know it, you can easily recreate the prefix. If you don't have it, you need to preserve and send it in the GET request (by escaping it) or you won't be able to use your image (for instance, you don't know how if the base64 represents a jpg or a png) – gbalduzzi Jun 20 '19 at 10:16
1

You need to know how HTTP works to understand why what you're doing is not going to work.

HTTP is the way browsers and Web servers (which can run PHP code) communicate.

One important thing to keep in mind here is that between any two Web pages there is no state to share for the Web server -- this is why HTTP is often called a stateless protocol. That's a good thing -- it saves on a lot of unneeded complexity for the Web server (and PHP).

I would not recommend attempting to circumvent the stateless nature of HTTP with things like window.localStorage (or sessionStorage for that matter) -- what if the user has loaded several instances of the same Web page on your website? How are you going to know which objects in your storage correspond to which upload workflow(s)?

I've seen many people try to do that, and it always breaks eventually.

There are several good ways to solve the "stateless" problem in your case -- how can a file uploaded with one Web page, be accessible to another, and yet making the file also available to the Web server?

In this answer I want to cover a so-called single Web page application solution, where you simply do away with two Web pages and instead have one Web page that does everything! This way you keep the state on the client but also in a clean manner that aligns well with how HTTP actually works:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <form enctype="multipart/form-data" method="post" action="http://httpbin.org/post">
            <label for="file-input">Select an image file</label>
            <input accept="image/*" id="file-input" name="image" type="file">
            <label for="file-selection-preview-image">Selected image</label>
            <img id="file-selection-preview-image"><!-- this element will display selected image. -->
            <input type="submit">
            <script>
                const form = document.currentScript.parentElement, file_input = form.elements.image;
                file_input.addEventListener("change", function(ev) { /// Whenever the selection changes
                    const files = ev.target.files;
                    console.assert(files.length == 1); /// Assert that there is only one file selected
                    console.assert(files[0].type.startsWith("image/")); /// Assert that the selected file is an image
                    const image = form.querySelector("img");
                    if(image.src) URL.revokeObjectURL(image.src); /// The kind of URLs we are dealing with refer to in-memory objects, so we have to dispose of them when we no longer need them -- the user agent does not do this for us automatically.
                    image.src = URL.createObjectURL(files[0]); /// Display selected file with the `img` element
                });
                /// User agent is free to retain file selection even after the Web page has been re-loaded, so if there is [a selection], we fire a "change" event manually so that the handler defined above may reflect this as it ordinarily would.
                if(file_input.files.length) file_input.dispatchEvent(new Event("change", { bubbles: true }));
            </script>
        </form>
    </body>
</html>

The above HTML document is set up with a simple form (which will let you upload files to your Web server even without JavaScript enabled). It contains a file input control that lets you actually select files (a single image file, since the input element does not contain the multiple attribute and specifies image/* for the accept attribute).

However, it also contains an img element which a script uses to load and display the selected image file with, immediately upon selection. I understand from your question that this may satisfy your requirement of accessing the selected file. The file is a Blob so you can do what you want with it, including drawing it in a canvas and modifying it otherwise. Here I just display it as an image.

The interesting property of the Web page is that while preview of the selected image only works through JavaScript, the form will still submit the selected file even without JavaScript. The user agent will post form data (image) to a convenient test server (at http://httpbin.com/post), which just happens to echo back your uploaded content in the form of a JSON file. With own service, you can handle the data yourself.

This solves the problem of multiple Web pages needing to share access to selected file, which would, if implemented properly, at least require uploading the file first and then accessing it from a URL the upload establishes on your Web server. Not necessarily more complicated than the solution in this answer, in fact arguably less complicated because it's how it has been done before JavaScript started allowing the kind of things I make use of in the document pasted above. But I would argue that this answer that covers a so-called single-page Web application, should fit you fine in this day and age.

That said, I consider a pure PHP solution to be interesting as well, and can pen up another answer should anyone wish to see one.

Armen Michaeli
  • 8,625
  • 8
  • 58
  • 95
  • I don't think to have completely got the answer, I'm so sorry, because you gave a lot of details. From your solution I've tried to call `URL.createObjectURL(file)` where file is `var file = document.querySelector('input[type=file]').files[0]` and pass the URL via GET to next page, but what I get is this `blob:http://testdecoridea2.altervista.org/b8c6ec17-ee0b-4e3e-95fa-3657955158e7` that I've put in the _src_ attribute of an image tag. I suppose that should be a sort of converter BLOB --> Image, isn't it? – chaw359 Jun 20 '19 at 10:11
  • 1
    You need to read the entire answer -- I specifically mention that this is a single page solution -- you *cannot* use the URL outside the user agent where the URL was created, much less on another host (Web server), it's a URL (uniform resource location) of an image that *only* resides in memory of the user agent where the URL is created (with `createObjectURL`). `GET` is for *getting* resources that are available to a Web server, how is your image available to the Web server before you even upload it there? That's why I mentioned you need to know how HTTP works to design your application. – Armen Michaeli Jun 20 '19 at 10:22