PHP从Android将JSON数据插入MySQL数据库

Posted

技术标签:

【中文标题】PHP从Android将JSON数据插入MySQL数据库【英文标题】:PHP Inserting JSON Data into MySQL database from Android 【发布时间】:2013-05-17 01:22:47 【问题描述】:

我有一个 android 应用程序,它通过 HTTP POST 方法将数据发送到 php 脚本,并试图解析数据以存储到 mysql 中。 Android 应用程序不是由我编写的,但我可以访问下面包含的源代码;它发送打包为 JSON 数组的数据。我的 PHP 脚本现在尝试将数据放入文本文件和 MySQL 数据库中:

<?php

   $con=mysqli_connect("localhost","dbname","dbpassword","table");
   if (mysqli_connect_errno())
   
     echo "Failed to connect to MySQL DB: " . mysqli_connect_error();
   

   $filename = __DIR__.DIRECTORY_SEPARATOR."jsontest.txt";

   $postdata = file_get_contents("php://input"); 
   $data = json_decode($postdata, true);


   //if (array_key_exists('records', $data) && is_array($data['records'])) 
   //above if loop threw warning "array_key_exists() expects parameter 2 to be array,  null given"

   if (is_array($data['records'])) 
      foreach ($data['records'] as $record) 
        $name = $record['name'];
        $value = $record['value'];
    $event = $record['event'];
        $timestamp = $record['timestamp'];

        file_put_contents($filename, "$name -> $value with event: $event at $timestamp\n", FILE_APPEND);
        mysqli_query($con,"INSERT INTO `Driving Data`(`Name`, `Value`, `Event`, `Timestamp`) VALUES ($name, $value, $event, $timestamp)");
      
   

   mysqli_close($con);
?>

文本文件按照我的指示打印出所有数据。但是,该数据库根本不会更新。数据库设置如下:

CREATE TABLE `Driving Data` (
 `Name` varchar(75) NOT NULL,
 `Value` varchar(40) NOT NULL,
 `Event` varchar(20) NOT NULL,
 `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=MyISAM DEFAULT CHARSET=latin1

以下是 JSON 数据示例的外观:

"records":["name":"accelerator_pedal_position","value":15.400001,"timestamp":1367598908.278000,"name":"engine_speed","value":1716.0,"timestamp":1367598908.285000,"name":"vehicle_speed","value":32.040001,"timestamp":1367598908.290000,"name":"brake_pedal_status","value":false,"timestamp":1367598908.293000,"name":"fuel_consumed_since_restart","value":0.147325,"timestamp":1367598908.301000,"name":"transmission_gear_position","value":"third","timestamp":1367598908.304000,"name":"steering_wheel_angle","value":-2.3733,"timestamp":1367598908.307000,"name":"fuel_consumed_since_restart","value":0.14745,"timestamp":1367598908.314000,"name":"transmission_gear_position","value":"third","timestamp":1367598908.317000,"name":"door_status","value":"driver","event":false,"timestamp":1367598908.320000,"name":"door_status","value":"passenger","event":false,"timestamp":1367598908.326000,"name":"door_status","value":"rear_left","event":false,"timestamp":1367598908.329000,"name":"door_status","value":"rear_right","event":false,"timestamp":1367598908.331000,"name":"odometer","value":0.0,"timestamp":1367598908.338000,"name":"high_beam_status","value":false,"timestamp":1367598908.341000,"name":"steering_wheel_angle","value":-2.3733,"timestamp":1367598908.343000,"name":"engine_speed","value":1716.0,"timestamp":1367598908.351000,"name":"powertrain_torque","value":74.0,"timestamp":1367598908.358000,"name":"accelerator_pedal_position","value":12.1,"timestamp":1367598908.364000,"name":"latitude","value":42.293911,"timestamp":1367598908.367000,"name":"longitude","value":-83.238762,"timestamp":1367598908.376000,"name":"engine_speed","value":1718.0,"timestamp":1367598908.380000,"name":"vehicle_speed","value":32.200001,"timestamp":1367598908.382000,"name":"brake_pedal_status","value":false,"timestamp":1367598908.391000,"name":"transmission_gear_position","value":"third","timestamp":1367598908.393000] 

所以我对 PHP 比较陌生,我似乎无法弄清楚问题是什么(即为什么数据库没有用这些值更新,但它仍然可以将它们写到文本文件中。供参考,如下是用于将数据发送到 Web 服务器的 Android 代码(我无法更改):

public class UploaderSink extends ContextualVehicleDataSink 
private final static String TAG = "UploaderSink";
private final static int UPLOAD_BATCH_SIZE = 25;
private final static int MAXIMUM_QUEUED_RECORDS = 5000;
private final static int HTTP_TIMEOUT = 5000;

private URI mUri;
private BlockingQueue<String> mRecordQueue =
        new LinkedBlockingQueue<String>(MAXIMUM_QUEUED_RECORDS);
private Lock mQueueLock = new ReentrantLock();
private Condition mRecordsQueued = mQueueLock.newCondition();
private UploaderThread mUploader = new UploaderThread();

/**
 * Initialize and start a new UploaderSink immediately.
 *
 * @param uri the URI to send HTTP POST requests to with the JSON data.
 */
public UploaderSink(Context context, URI uri) 
    super(context);
    mUri = uri;


public UploaderSink(Context context, String path) throws DataSinkException 
    this(context, uriFromString(path));


@Override
public void stop() 
    super.stop();
    mUploader.done();


public boolean receive(RawMeasurement measurement) 
    String data = measurement.serialize(true);
    mRecordQueue.offer(data);
    if(mRecordQueue.size() >= UPLOAD_BATCH_SIZE) 
        mQueueLock.lock();
        mRecordsQueued.signal();
        mQueueLock.unlock();
    
    return true;


/**
 * Returns true if the path is not null and if it is a valid URI.
 *
 * @param path a URI to validate
 * @return true if path is a valid URI.
 *
 */
public static boolean validatePath(String path) 
    if(path == null) 
        Log.w(TAG, "Uploading path not set (it's " + path + ")");
        return false;
    

    try 
        uriFromString(path);
        return true;
     catch(DataSinkException e) 
        return false;
    


@Override
public String toString() 
    return Objects.toStringHelper(this)
        .add("uri", mUri)
        .add("queuedRecords", mRecordQueue.size())
        .toString();


private static URI uriFromString(String path) throws DataSinkException 
    try 
        return new URI(path);
     catch(java.net.URISyntaxException e) 
        throw new UploaderException(
            "Uploading path in wrong format -- expected: ip:port");
    


private static class UploaderException extends DataSinkException 
    private static final long serialVersionUID = 7436279598279767619L;

    public UploaderException()  

    public UploaderException(String message) 
        super(message);
    


private class UploaderThread extends Thread 
    private boolean mRunning = true;

    public UploaderThread() 
        start();
    

    public void run() 
        while(mRunning) 
            try 
                ArrayList<String> records = getRecords();
                String data = constructRequestData(records);
                HttpPost request = constructRequest(data);
                makeRequest(request);
             catch(UploaderException e) 
                Log.w(TAG, "Problem uploading the record", e);
             catch(InterruptedException e) 
                Log.w(TAG, "Uploader was interrupted", e);
                break;
            
        
    

    public void done() 
        mRunning = false;
    

    private String constructRequestData(ArrayList<String> records)
            throws UploaderException 
        StringWriter buffer = new StringWriter(512);
        JsonFactory jsonFactory = new JsonFactory();
        try 
            JsonGenerator gen = jsonFactory.createJsonGenerator(buffer);

            gen.writeStartObject();
            gen.writeArrayFieldStart("records");
            Iterator<String> recordIterator = records.iterator();
            while(recordIterator.hasNext()) 
                gen.writeRaw(recordIterator.next());
                if(recordIterator.hasNext()) 
                    gen.writeRaw(",");
                
            
            gen.writeEndArray();
            gen.writeEndObject();

            gen.close();
         catch(IOException e) 
            Log.w(TAG, "Unable to encode all data to JSON -- " +
                    "message may be incomplete", e);
            throw new UploaderException();
        
        return buffer.toString();
    

    private HttpPost constructRequest(String data)
            throws UploaderException 
        HttpPost request = new HttpPost(mUri);
        try 
            ByteArrayEntity entity = new ByteArrayEntity(
                    data.getBytes("UTF8"));
            entity.setContentEncoding(
                    new BasicHeader("Content-Type", "application/json"));
            request.setEntity(entity);
         catch(UnsupportedEncodingException e) 
            Log.w(TAG, "Couldn't encode records for uploading", e);
            throw new UploaderException();
        
        return request;
    

    private void makeRequest(HttpPost request) throws InterruptedException 
        HttpParams parameters = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(parameters, HTTP_TIMEOUT);
        HttpConnectionParams.setSoTimeout(parameters, HTTP_TIMEOUT);
        final HttpClient client = new DefaultHttpClient(parameters);
        try 
            HttpResponse response = client.execute(request);
            final int statusCode = response.getStatusLine().getStatusCode();
            if(statusCode != HttpStatus.SC_CREATED) 
                Log.w(TAG, "Got unxpected status code: " + statusCode);
            
         catch(IOException e) 
            Log.w(TAG, "Problem uploading the record", e);
            try 
                Thread.sleep(5000);
             catch(InterruptedException e2) 
                Log.w(TAG, "Uploader interrupted after an error", e2);
                throw e2;
            
        
    

    private ArrayList<String> getRecords() throws InterruptedException 
        mQueueLock.lock();
        if(mRecordQueue.isEmpty()) 
            // the queue is already thread safe, but we use this lock to get
            // a condition variable we can use to signal when a batch has
            // been queued.
            mRecordsQueued.await();
        

        ArrayList<String> records = new ArrayList<String>();
        mRecordQueue.drainTo(records, UPLOAD_BATCH_SIZE);

        mQueueLock.unlock();
        return records;
    

【问题讨论】:

发送代码不应该相关,只有接收代码和输入数据。请以插入所有输入变量的形式发布您的查询字符串。 您应该为 mysqli_query 添加一些错误检查,它将帮助您调试问题。 php.net/manual/en/mysqli.query.php 您遇到的错误是什么?还要提及您的表格列的数据类型 此外,您的插入语句中没有引用值。而且它很容易受到 SQL 注入的攻击。 我没有收到任何错误。刚刚将 MySQL 列的数据类型添加到帖子中 【参考方案1】:

尝试将 unix 时间戳转换为 mysql 时间戳

if (is_array($data['records'])) 
  foreach ($data['records'] as $record) 
    $name = $record['name'];
    $value = $record['value'];
$event = $record['event'];
    $timestamp = date('Y-m-d H:i:s',$record['timestamp']);

    file_put_contents($filename, "$name -> $value with event: $event at $timestamp\n", FILE_APPEND);
    mysqli_query($con,"INSERT INTO `Driving Data`(`Name`, `Value`, `Event`, `Timestamp`) VALUES ($n

ame, $value, $event, $timestamp)");
      
   

【讨论】:

这引起了我的问题,时间戳的格式不正确。感谢您的帮助!【参考方案2】:

我认为你的 SQL 语法是错误的。看这一行:

mysqli_query($con,"INSERT INTO `Driving Data`(`Name`, `Value`, `Event`, `Timestamp`) VALUES ($name, $value, $event, $timestamp)");

如果任何变量(例如 $name)有空格,你的 SQL 将是这样的:

INSERT INTO `Driving Data`(`Name`, `Value`, `Event`, `Timestamp`) VALUES (Name with spaces, ...)

但它应该在单引号之间:

INSERT INTO `Driving Data`(`Name`, `Value`, `Event`, `Timestamp`) VALUES ('Name with spaces', ...)

我没有对此进行测试,但我认为这是您问题的根源。我认为这是错误行,因为正如您所说,创建了文本文件(因此之前的 file_put_contents 运行正常)。

另外,您为什么不对插入的行使用某种“ID”?我会添加一个自动递增的“id”列作为主键。

【讨论】:

你可以使用 if (!mysqli_query($con, ...)) die("SQL error: " .mysqli_error($con));检查 SQL 错误【参考方案3】:

现在,只要看看你的代码,你在 INSERT 命令中写了两次 VALUES。

目前显示为:...Timestamp) VALUES VALUES ($name,...

删除第二次出现的 VALUES 并查看它是否有效。它从来没有抛出错误消息?

【讨论】:

哦,我想我在将代码放在帖子中时不小心复制了另一个“VALUES”,这实际上不在我正在测试的代码中(所以不会引起问题)。我从帖子中删除了它 啊,好吧。您可能需要在变量前后添加单引号,除非它是数字。像这样: VALUES ('$name', '$value', '$event', '$timestamp') 我不相信浮点数需要被引用,但测试一下并删除它们周围的引号t. 如果数据被写入文件,那么这绝对不是 Android 问题。您是否尝试仅插入值 1 而不是变量,以查看查询是否甚至出于调试目的与数据库通信?也许在 mysqli_query 函数之后直接将 die() 添加到尾部。像这样:mysqli_query() or die('Query didn't work.');看看没有工作的声明是否回来了。也许这个链接也会有所帮助? ***.com/questions/2304894/…

以上是关于PHP从Android将JSON数据插入MySQL数据库的主要内容,如果未能解决你的问题,请参考以下文章

从 iOS 获取我的 JSON 以发布到我的 php 文件并插入到 mySQL 时遇到问题

无法使用 Kotlin Retrofit 将数据从 Android 插入到 Mysql [重复]

使用 php 将 JSON 中的数据插入 mysql

通过 JSON 将数据从 android 发送到服务器

如何优化使用 PHP 或 Mysql 或 Laravel 将 12K 的 JSON 插入数据库

将 POST 数据从 Android 应用程序发送到 PHP MySQL