顯示具有 javascript 標籤的文章。 顯示所有文章
顯示具有 javascript 標籤的文章。 顯示所有文章

2025年6月7日 星期六

實作物聯網網站用Django+AJAX+JavaScript整合MQTT

 

1.安裝套件

pip install django

pip install paho-mqtt

2.建立mqtttest專案

django-admin startproject mqtttest

3.進入專案

cd mqtttest

4.建立app

python manage.py startapp mqttapp

5.修改mqtttest/mqtttest/settings.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"""
Django settings for mqtttest project.

Generated by 'django-admin startproject' using Django 4.2.9.

For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-q2ia=(7x3d5_mxao#-sl)+)crp9j%e*732fg4jhid-f&sln5k('

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'mqttapp',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'mqtttest.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'mqtttest.wsgi.application'


# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

MQTT_SERVER = 'broker.MQTTGO.io'
MQTT_PORT = 1883
MQTT_KEEPALIVE = 60
MQTT_USER = ''
MQTT_PASSWORD = ''

6.修改mqtttest/mqtttest/urls.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
"""
URL configuration for mqtttest project.

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from mqttapp.views import home, publish_message, mqtt_messages_json

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home),
    path('publish/', publish_message, name='publish'),
    path('api/messages/', mqtt_messages_json, name='mqtt_messages_json'),
]

7.mqtttest/mqttapp/models.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from django.db import models

# Create your models here.

class MQTTMessage(models.Model):
    received_at = models.DateTimeField(auto_now_add=True)  # 自動紀錄訊息接收時間
    topic = models.CharField(max_length=255)
    payload = models.TextField()

    def __str__(self):
        return f"{self.received_at} - {self.topic}"

8.mqtttest/mqttapp/admin.py

1
2
3
4
5
6
7
8
9
from django.contrib import admin
from .models import MQTTMessage

@admin.register(MQTTMessage)
class MQTTMessageAdmin(admin.ModelAdmin):
    list_display = ('received_at', 'topic', 'payload')  # 可自訂顯示欄位
    list_filter = ('topic',)
    search_fields = ('topic', 'payload')
    ordering = ('-received_at',)

9.mqtttest/mqttapp/views.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from django.shortcuts import render

# Create your views here.
import json
from django.http import JsonResponse
from mqttapp.models import MQTTMessage
from django.views.decorators.csrf import csrf_exempt
import paho.mqtt.client as mqtt

def home(request):
	return render(request, 'index.html')

@csrf_exempt
def publish_message(request):
    if request.method == 'POST':
        try:
            data = json.loads(request.body)
            topic = data.get('topic', 'django/mqtt')
            msg = data.get('msg', 'test')

            # 即時建立連線並發送
            client = mqtt.Client()
            client.connect('broker.MQTTGO.io', 1883, 60)
            rc, mid = client.publish(topic, msg)
            client.disconnect()

            return JsonResponse({'code': rc})
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=400)
    return JsonResponse({'error': 'Only POST allowed'}, status=405)

def mqtt_messages_json(request):
    messages = MQTTMessage.objects.order_by('-received_at')[:20]
    data = [
        {
            'topic': m.topic,
            'payload': m.payload,
            'received_at': m.received_at.strftime('%Y-%m-%d %H:%M:%S')
        }
        for m in messages
    ]
    return JsonResponse(data, safe=False)

10.在mqtttest下建立templates資料夾,並新增mqtttest/templates/index.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!DOCTYPE html>
<html>
	<head>
		<title>MQTT 測試</title>
		<script>
			function loadMessages() {
				fetch('/api/messages/')
					.then(response => response.json())
					.then(data => {
						const tbody = document.getElementById('messages');
						tbody.innerHTML = '';
						data.forEach(item => {
							const row = `<tr>
								<td>${item.received_at}</td>
								<td>${item.topic}</td>
								<td>${item.payload}</td>
							</tr>`;
							tbody.innerHTML += row;
						});
					});
				}
			function publishMessage() {
				const topic = document.getElementById('topic').value;
				const msg = document.getElementById('msg').value;

				fetch('/publish/', {
					method: 'POST',
					headers: { 'Content-Type': 'application/json' },
					body: JSON.stringify({ topic, msg })
				})
				.then(response => response.json())
				.then(data => {
					alert('已送出 MQTT 訊息!');
				})
				.catch(error => {
					alert('錯誤:' + error);
				});
			}
			setInterval(loadMessages, 3000);
			window.onload = loadMessages;
		</script>
	</head>
	<body>
		<h1>MQTT 測試</h1>
		<h2><a href="https://broker.mqttgo.io/">使用MQTT GO伺服器</a></h2>
		<h2>發送 MQTT 訊息</h2>
		<label>Topic: <input id="topic" value="django/mqtt"></label><br>
		<label>訊息: <input id="msg" value="Hello MQTT!"></label><br>
		<button onclick="publishMessage()">送出</button>
		<h2>最新接收訊息</h2>
		<table border="1">
			<thead>
				<tr><th>時間</th><th>主題</th><th>訊息</th></tr>
			</thead>
			<tbody id="messages"></tbody>
		</table>
	</body>
</html>

12.在mqtttest根目錄下建立mqtt_listener.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import os
import django
import paho.mqtt.client as mqtt

# 初始化 Django 環境
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mqtttest.settings")
django.setup()

from mqttapp.models import MQTTMessage
from django.conf import settings

def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("Connected to MQTT broker.")
        client.subscribe("django/mqtt")
    else:
        print("Connection failed with code", rc)

def on_message(client, userdata, msg):
    payload = msg.payload.decode('utf-8')
    print(f"Received message on topic: {msg.topic}, payload: {payload}")
    MQTTMessage.objects.create(topic=msg.topic, payload=payload)

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.username_pw_set(settings.MQTT_USER, settings.MQTT_PASSWORD)
client.connect(settings.MQTT_SERVER, settings.MQTT_PORT, settings.MQTT_KEEPALIVE)

client.loop_forever()

13.回到命令列,做資料庫同步,並建立超級使用者
python manage.py makemigrations
python manage.py migrate

14.執行網站
python manage.py runserver



15.再打啟另一個命令列
python mqtt_listener.py



16.啟動MQTT GO,連線並訂閱django/mqtt



17.打開瀏覽器進行測試,如最上圖

2024年7月22日 星期一

micro:bit 十種模式的燈條控制

這個程式控制一個 NeoPixel 燈條,實現了 10 種不同的顯示模式。以下是每個模式的解釋:

  • 模式 0: 清除顯示

清除燈條上的顏色,將所有燈熄滅。

  • 模式 1: 單色顯示

根據 submode 的值顯示不同的顏色。顏色從紅色開始,依次是橙色、黃色、綠色、藍色、靛藍色、紫色、紫羅蘭色、白色和黑色。

  • 模式 2: 單色顯示(自動變換顏色)

與模式 1 相同,但每 500 毫秒 submode 自動增加,改變顏色。

  • 模式 3: 彩虹旋轉

燈條上的顏色進行旋轉。

  • 模式 4: 移動彩虹

彩虹顏色向左移動,並在特定條件下重置顯示。

  • 模式 5: 部分顯示彩虹

在燈條的前部分顯示彩虹,範圍根據 ShiftCount 增加。

  • 模式 6: 單個 LED 顯示彩虹

每次僅在一個 LED 上顯示彩虹顏色,位置根據 ShiftCount 變化。

  • 模式 7: 隨機顏色顯示

隨機選擇一個 LED 顯示隨機顏色。

  • 模式 8: 聲音圖表

根據環境聲音的強度顯示條形圖。

  • 模式 9: 亮度圖表

根據環境光亮度顯示條形圖。


按鈕控制:

  • 按鈕 A:

切換模式,每次按下時 mode 增加 1,顯示當前模式。

  • 按鈕 B:

切換 submode,每次按下時 submode 增加 1,影響單色顯示的顏色。


主循環:

根據當前的 mode 執行對應的顯示模式。


積木程式:


JavaScript程式:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
function rainbow2 () {
    strip.show()
    strip.shift(1)
    basic.pause(100)
    ShiftCount = (ShiftCount + 1) % (Number2 + 1)
    if (ShiftCount == 0) {
        strip.showRainbow(1, 360)
    }
}
function rainbow3 () {
    strip.clear()
    range = strip.range(0, ShiftCount)
    range.showRainbow(1, 360)
    range.show()
    ShiftCount = (ShiftCount + 1) % (Number2 + 1)
    basic.pause(100)
}
function monochrome () {
    if (submode == 0) {
        strip.showColor(neopixel.colors(NeoPixelColors.Red))
    } else if (submode == 1) {
        strip.showColor(neopixel.colors(NeoPixelColors.Orange))
    } else if (submode == 2) {
        strip.showColor(neopixel.colors(NeoPixelColors.Yellow))
    } else if (submode == 3) {
        strip.showColor(neopixel.colors(NeoPixelColors.Green))
    } else if (submode == 4) {
        strip.showColor(neopixel.colors(NeoPixelColors.Blue))
    } else if (submode == 5) {
        strip.showColor(neopixel.colors(NeoPixelColors.Indigo))
    } else if (submode == 6) {
        strip.showColor(neopixel.colors(NeoPixelColors.Violet))
    } else if (submode == 7) {
        strip.showColor(neopixel.colors(NeoPixelColors.Purple))
    } else if (submode == 8) {
        strip.showColor(neopixel.colors(NeoPixelColors.White))
    } else if (submode == 9) {
        strip.showColor(neopixel.colors(NeoPixelColors.Black))
    }
    strip.show()
}
input.onButtonPressed(Button.A, function () {
    mode = (mode + 1) % maxMode
    basic.showString("" + (mode))
    if (mode == 3 && mode <= 4) {
        strip = neopixel.create(DigitalPin.P0, Number2, NeoPixelMode.RGB)
        strip.showRainbow(1, 360)
    } else if (mode >= 5) {
        strip.clear()
        strip.show()
    }
})
function loudness () {
    strip.clear()
    strip.showBarGraph(Math.map(input.soundLevel(), 0, 255, 0, 16), 16)
    strip.show()
}
function rainbow5 () {
    strip.clear()
    range = strip.range(randint(0, Number2 - 1), 1)
    range.showColor(randint(0, 65535))
    range.show()
    ShiftCount = (ShiftCount + 1) % (Number2 + 1)
    basic.pause(200)
}
function rainbow () {
    strip.rotate(3)
    strip.show()
    basic.pause(100)
}
function rainbow4 () {
    strip.clear()
    range = strip.range(ShiftCount, 1)
    range.showRainbow(1, 360)
    range.show()
    ShiftCount = (ShiftCount + 1) % (Number2 + 1)
    basic.pause(100)
}
input.onButtonPressed(Button.B, function () {
    submode = (submode + 1) % 10
})
function brightness () {
    strip.clear()
    strip.showBarGraph(Math.map(input.lightLevel(), 0, 255, 0, 16), 16)
    strip.show()
}
let range: neopixel.Strip = null
let strip: neopixel.Strip = null
let ShiftCount = 0
let Number2 = 0
let submode = 0
let maxMode = 0
let mode = 0
mode = 0
maxMode = 10
submode = 0
Number2 = 16
ShiftCount = 0
strip = neopixel.create(DigitalPin.P0, Number2, NeoPixelMode.RGB)
basic.showString("" + (mode))
basic.forever(function () {
    if (mode == 0) {
        strip.clear()
        strip.show()
    } else if (mode == 1) {
        monochrome()
    } else if (mode == 2) {
        monochrome()
        basic.pause(500)
        submode = (submode + 1) % 10
    } else if (mode == 3) {
        rainbow()
    } else if (mode == 4) {
        rainbow2()
    } else if (mode == 5) {
        rainbow3()
    } else if (mode == 6) {
        rainbow4()
    } else if (mode == 7) {
        rainbow5()
    } else if (mode == 8) {
        loudness()
    } else {
        brightness()
    }
})

Python程式:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def rainbow2():
    global ShiftCount
    strip.show()
    strip.shift(1)
    basic.pause(100)
    ShiftCount = (ShiftCount + 1) % (Number2 + 1)
    if ShiftCount == 0:
        strip.show_rainbow(1, 360)
def rainbow3():
    global range2, ShiftCount
    strip.clear()
    range2 = strip.range(0, ShiftCount)
    range2.show_rainbow(1, 360)
    range2.show()
    ShiftCount = (ShiftCount + 1) % (Number2 + 1)
    basic.pause(100)
def monochrome():
    if submode == 0:
        strip.show_color(neopixel.colors(NeoPixelColors.RED))
    elif submode == 1:
        strip.show_color(neopixel.colors(NeoPixelColors.ORANGE))
    elif submode == 2:
        strip.show_color(neopixel.colors(NeoPixelColors.YELLOW))
    elif submode == 3:
        strip.show_color(neopixel.colors(NeoPixelColors.GREEN))
    elif submode == 4:
        strip.show_color(neopixel.colors(NeoPixelColors.BLUE))
    elif submode == 5:
        strip.show_color(neopixel.colors(NeoPixelColors.INDIGO))
    elif submode == 6:
        strip.show_color(neopixel.colors(NeoPixelColors.VIOLET))
    elif submode == 7:
        strip.show_color(neopixel.colors(NeoPixelColors.PURPLE))
    elif submode == 8:
        strip.show_color(neopixel.colors(NeoPixelColors.WHITE))
    elif submode == 9:
        strip.show_color(neopixel.colors(NeoPixelColors.BLACK))
    strip.show()

def on_button_pressed_a():
    global mode, strip
    mode = (mode + 1) % maxMode
    basic.show_string("" + str((mode)))
    if mode == 3 and mode <= 4:
        strip = neopixel.create(DigitalPin.P0, Number2, NeoPixelMode.RGB)
        strip.show_rainbow(1, 360)
    elif mode >= 5:
        strip.clear()
        strip.show()
input.on_button_pressed(Button.A, on_button_pressed_a)

def loudness():
    strip.clear()
    strip.show_bar_graph(Math.map(input.sound_level(), 0, 255, 0, 16), 16)
    strip.show()
def rainbow5():
    global range2, ShiftCount
    strip.clear()
    range2 = strip.range(randint(0, Number2 - 1), 1)
    range2.show_color(randint(0, 65535))
    range2.show()
    ShiftCount = (ShiftCount + 1) % (Number2 + 1)
    basic.pause(200)
def rainbow():
    strip.rotate(3)
    strip.show()
    basic.pause(100)
def rainbow4():
    global range2, ShiftCount
    strip.clear()
    range2 = strip.range(ShiftCount, 1)
    range2.show_rainbow(1, 360)
    range2.show()
    ShiftCount = (ShiftCount + 1) % (Number2 + 1)
    basic.pause(100)

def on_button_pressed_b():
    global submode
    submode = (submode + 1) % 10
input.on_button_pressed(Button.B, on_button_pressed_b)

def brightness():
    strip.clear()
    strip.show_bar_graph(Math.map(input.light_level(), 0, 255, 0, 16), 16)
    strip.show()
range2: neopixel.Strip = None
strip: neopixel.Strip = None
ShiftCount = 0
Number2 = 0
submode = 0
maxMode = 0
mode = 0
mode = 0
maxMode = 10
submode = 0
Number2 = 16
ShiftCount = 0
strip = neopixel.create(DigitalPin.P0, Number2, NeoPixelMode.RGB)
basic.show_string("" + str((mode)))

def on_forever():
    global submode
    if mode == 0:
        strip.clear()
        strip.show()
    elif mode == 1:
        monochrome()
    elif mode == 2:
        monochrome()
        basic.pause(500)
        submode = (submode + 1) % 10
    elif mode == 3:
        rainbow()
    elif mode == 4:
        rainbow2()
    elif mode == 5:
        rainbow3()
    elif mode == 6:
        rainbow4()
    elif mode == 7:
        rainbow5()
    elif mode == 8:
        loudness()
    else:
        brightness()
basic.forever(on_forever)

2023年4月9日 星期日

Using MQTT commands to control the action of animation in Three.js



1. three.js example: 
three.js examples (threejs.org)

2.HIVEMQ: https://www.mqtt-dashboard.com/

3.MQTT example:物動作控制

4.Publish

Topic:AnimationCommand

Commands: Idle, Walking, Running, Dance, Death, Sitting, Standing, Jump, Yes, No, Wave, Punch, ThumbsUp

5.Subscribe:

Topic: AnimtionState

2023年1月27日 星期五

Dropzone.js讓網站上拖移文件變得相當容易

 Dropzone.js官方網站:Dropzone.js

範例一:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.2/min/dropzone.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.2/dropzone.min.css">
  </head>
  <body>
	<form action="/file-upload" method="post" enctype="multipart/form-data">
	  <input type="file" name="file" />
	</form>
  </body>
</html>

執行結果:
選取檔案前

選取檔案後

範例二:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<html>
	<head>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.2/min/dropzone.min.js"></script>
		<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.2/dropzone.min.css">
		<script>
		  Dropzone.options.myGreatDropzone = { // camelized version of the `id`
			paramName: "file", // The name that will be used to transfer the file
			autoProcessQueue: false,
			addRemoveLinks: true,			
			maxFilesize: 2, // MB
		  };
		</script>
	</head>
	<body>
		<form action="/target" class="dropzone" id="my-great-dropzone"></form>
	</body>
</html>

執行結果:
拖移檔案前

拖移檔案後


刪除檔案後