• Resolved pangdbbuilder

    (@pangdbbuilder)


    I got a function shows a picture. But when I try WordPress Ajax call to execute it, it returns <?php only, Is there any correct way to do it?

    --PHP code
    function get_image(){
    $file='Large-Sample-Image-download-for-Testing.jpg';
    //make it downloadable
    header("content-disposition: attachment; filename=test.jpg");
    header('cache-control: must-revalidate');
    header("expires: 0");
    //return file contents
    readfile($file);
    }
    
    --HTML page
    <htmL>
    <head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js" ></script>
    <script>
    jQuery(document).ready(function($) { 
    	$("#ajax_get_image_button").click(function() { 		  
    		var xhr = new XMLHttpRequest();
    		xhr.open('POST','get_image.php', true);
    		xhr.responseType = "blob"; 
    		xhr.onload = function() {
    			var img = document.createElement('img');
    			img.src=URL.createObjectURL(this.response); 
    			document.body.append(img);
    		};
    		xhr.send();
    	});
    });
    </script>
    </head>
    <body>
    <input type="button" id="ajax_get_image_button" name="ajax_get_image_button" value="Ajax call to get image" />
    </body>
    </html>
Viewing 10 replies - 1 through 10 (of 10 total)
  • Thread Starter pangdbbuilder

    (@pangdbbuilder)

    I just noticed even WordPress Ajax call can return non-binary data, i.e JSON, it would still give <?php before data. very strange. I have to dig out what WordPress hook doing…

    Moderator bcworkz

    (@bcworkz)

    I don’t see where WP comes into play in handling the Ajax request. get-image.php is not a WP file and your get_image() function uses no WP PHP resources.

    Anyway, you need to send a Content-Type: header that reflects the correct MIME type for the file, such as image/jpeg. For generic binary files, use application/octet-stream

    FWIW, it you were to involve WP resources in your PHP Ajax handler, you’re Doing It Wrong? ?? Your approach is fine when no WP resources are involved. If you’re interested, WP has a unique way of handling Ajax. Also, within WP you should not use jQuery from outside sources. You should use the jQuery that is shipped with WP, otherwise conflicts could arise with other code that uses jQuery.

    Thread Starter pangdbbuilder

    (@pangdbbuilder)

    @bcworkz

    actually my initial post is working code without wordPress. Here my not-working code

    add_action('admin_enqueue_scripts','my_javascript');
    function my_javascript($hook) {
        global $current_screen;
        if( $hook !== $current_screen->base ) return; //my page see this script only
            wp_enqueue_script( 
            'my_js', 
            plugins_url( 'js/get_image.js', __FILE__ ),
            array('jquery'), 
            '1.0.0',
        );
        wp_localize_script(
            'my_js',
            'my_js_obj',
            array(
                'ajax_url' => admin_url('admin-ajax.php' ),
                'nonce'    => wp_create_nonce( 'my_nonce' ),
            )
        );
    
    }
    ...
    add_action( 'wp_ajax_my_get_image', 'func_get_image' );
    function func_get_image(){
        check_ajax_referer( 'my_nonce');
        get_image();
        wp_die();
    }
    
    

    and the javascript to send out Ajax

     $("#ajax_get_image_button").click(function() {        
            var data = {                    
                    _ajax_nonce: my_js_obj.nonce, 
                    action: "func_get_image",
                };
            url=my_js_obj.ajax_url;
            //xhr
            var xhr = new XMLHttpRequest();
            xhr.open('POST', url, true);
            xhr.responseType = "blob";
            xhr.onload = function() {
                var img = document.createElement('img');
                img.src=URL.createObjectURL(this.response); 
                document.body.append(img);
            };
    
            xhr.send($.param( data ));
        });

    WordPress return <?php only if it is real binary data. If I send back a text, WordPress return <?php..text data. I suspect it could be WP_HOOK issue but not sure.

    Alessandro Lin

    (@alessandro12)

    I have a solution without jQuery.
    Then you will also need to adapt the code to use with WP.
    With the Hooks:
    add_action( ‘wp_ajax_nopriv_your_action’, ‘your_function’ );
    add_action( ‘wp_ajax_your_action’, ‘your_function’ );

    -- file index.html--
    <!DOCTYPE html>
    <html>
    <head>
    	<title>Ajax Image Snip</title>
    	<meta name="viewport" content="width=device-width, initial-scale=1">
      	<meta charset="utf-8">	
    </head>
    <body>
    	<br><br>
    	<div style="margin: auto;width: 300px;">
    		<input style="height: 50px;" type="button" id="ajax_get_image_button" name="ajax_get_image_button" value="Ajax call to get image" onclick="get_img();" />
    	</div>
    	<script>
    		{
    			function get_img(){
    				let url='get_image.php';
    				let xhr = new XMLHttpRequest();
    				xhr.responseType = 'blob';
    	
    				xhr.onload = function() {
    				    if (xhr.status != 200) {
    					    alert( 'Error: ' + xhr.status);
    					    return;
    				    }
    	
    				    let img = document.createElement('img');
    				    let imgUrl = URL.createObjectURL(xhr.response);
    				    img.src = imgUrl;
    				    document.body.append(img);
    			    };
    	
    				xhr.open('GET', url);
    				xhr.send();
    			}
    	    }			
    	</script>
    </body>
    -- file get_image.php --
    <?php
    
    $file = 'ImageKey.jpg';
    
    if (file_exists($file)) {
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="'.basename($file).'"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file));
        readfile($file);
        exit;
    }

    If you want to try this it should work.

    https://captcha-ajax.eu/ajaxImageSnip/

    You can also get ajax with ” fetch ” which is more compact.

    Thread Starter pangdbbuilder

    (@pangdbbuilder)

    @alessandro12

    Yeah, you get my point. It is always keep working without WordPress until it use WordPress

    admin-ajax.php

    WordPress is not supposed to change Ajax call output data, but it does. seems it just filter out binary data ??

    I am still in finding why. I noticed if I added error_log() into wp-includes/plugin.php in function do_action() like following, it would add this error_log() output into Ajax call return output data as well. Weird.

    I guess WordPress could have some options to set up how to modify Ajax call output…no clues found so far.

    function do_action( $hook_name, …$arg ) {
    global $wp_filter, $wp_actions, $wp_current_filter;
    
    if($hook_name=='wp_ajax_my_get_image') { 
        error_log("\n " . var_dump($wp_filter[ $hook_name ]), 3, 'myDebug.txt');
    }
    ...
    Moderator bcworkz

    (@bcworkz)

    var_dump() doesn’t return a string, it generates output. That is how it ends up as Ajax return. To get something similar returned instead of output you’d do
    print_r( $wp_filter[ $hook_name ], true )

    I tried using your XMLHttpRequest code on my site (with appropriate adaptations) and I got a 400 bad request response. I don’t think sending the action value serialized with .param() puts the data in a format that WP can utilize. Maybe sending data as a formData object would be more compatible?

    I also tried using jQuery Ajax code I know works, but instead of the usual known good response from PHP I tried your get_image() code, again with appropriate adaptations. It didn’t work, but probably because my Ajax code wasn’t set up correctly to receive an image stream. However, my browser’s network developer tool shows the correct image was sent as a response. AFAICT WP does not alter output from the Ajax action callback. There’s not any way it could since output is sent directly.

    Thread Starter pangdbbuilder

    (@pangdbbuilder)

    @bcworkz good catch on print_r(). Would use it instead.

    But WordPress Ajax issue is still there. If your test passed, please share your finding and working code. I just don’t get it that working PHP code behavior weird in WordPress context.

    ?.param()? is fine for me. I have other Ajax calls work fine in WordPress except get downloadable binary data .

    Thread Starter pangdbbuilder

    (@pangdbbuilder)

    I figured out where <?php came from. I got one php file which started with <?php?php. Testing another round now.

    Thread Starter pangdbbuilder

    (@pangdbbuilder)

    Finally, I get it working. It is small thing but seems WordPress ask plugin code to read file like this specially. Good to know ??

    readfile(plugin_dir_path( __FILE__ ) . '/'. $file);
    
    Alessandro Lin

    (@alessandro12)

    Finally, I get it working.

    Okay, great! It was only necessary to put the right file path, then.

Viewing 10 replies - 1 through 10 (of 10 total)
  • The topic ‘Ajax call can’t get binary data’ is closed to new replies.